use crate::alu::SplitRegister;
use crate::alu::RegisterError;
use p101_is::instruction::*;
use rust_decimal::Decimal;

/// Inner representation of the "memory" register.
/// Hides implementation details and provides a safe
/// abstraction of the register status.
#[derive(Copy, Clone, Debug, PartialEq)]
enum Value {
    /// The register can be split
    R(SplitRegister),
    
    /// The register contains up to 11 instructions and a 11-digits number
    IR([Option<Instruction>; 11], SplitRegister),

    // The register contains 24 instructions
    I([Option<Instruction>; 24]),
}

impl Value {
    pub fn new() -> Value {
        Value::R(SplitRegister::new())
    }
}

/// P101 storage register (D, E, F) that may contain: 
/// - 24 memory locations
/// - a split register
/// - 11 instructions in the lower part (1-11) and half register (12-24).
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MemoryRegister {
    value: Value,
}

impl MemoryRegister {
    /// Register is initialized as "data" with value zero.
    pub fn new() -> MemoryRegister {
        MemoryRegister {
            value: Value::new(),
        }
    }

    /// Read the whole value of the register or the right part.
    /// Returns zero if the register contains instructions.
    /// Supposing to work on register F, this maps to F♦
    pub fn read(&self) -> Decimal {
        match self.value {
            Value::R(r) => r.read(),
            Value::IR(_, _) => 0.into(),
            Value::I(_) => 0.into(),
        }
    }

    /// Read the left part of the split register. Returns zero otherwise. 
    /// Supposing to be work on register f, this maps to f♦
    pub fn read_left(&self) -> Decimal {
        match self.value {
            Value::R(r) => r.read_left(),
            Value::IR(_, r) => r.read_left(),
            _ => 0.into(),
        }
    }

    /// Writes the value to the register. Different behavior occurs
    /// depending on register status.
    /// - Writing to a full register is always ok
    /// - Writing to a split register is ok the value does not overflow
    ///   exceeds capacity.
    /// - Does nothing if the register contains instructions.
    pub fn write(&mut self, value: Decimal) -> Result<(), RegisterError> {
        match self.value {
            Value::R(ref mut r) => r.write(value),
            _ => Ok(()),
        }
    }

    /// Writes the value to the left part of a split register.
    /// Different behavior occurs depending on register status.
    /// - Writing to a split register succeed if  the value does not overflow
    /// - Writing to a full register automatically splits it provided
    ///   its value does not overflow.
    /// - Does nothing if the register contains instructions.
    pub fn write_left(&mut self, value: Decimal) -> Result<(), RegisterError> {
        match self.value {
            Value::R(ref mut r) => r.write_left(value),
            Value::IR(_, ref mut r) => r.write_left(value),
            _ => Ok(()),
        }
    }

    /// Reads the instruction at the specified 0-based address.
    /// Returns a memory error if the register does not contain
    /// instructions or the address is out of bounds.
    pub fn read_instruction(&self, address: usize) -> Result<Option<Instruction>, RegisterError> {
        match self.value {
            Value::I(i) => {
                if address < 24 {
                    Ok(i[address])
                } else {
                    Err(RegisterError::NoMemory)
                }
            },
            Value::IR(i, _) => {
                if address < 11 {
                    Ok(i[address])
                } else {
                    Err(RegisterError::NoMemory)
                }
            },
            _ => Err(RegisterError::NoMemory),
        }
    }

    /// Writes an instruction at the 0-based address.
    /// if the register holds data it is converted into
    /// I or IR type, depending on the address.
    pub fn write_instruction(&mut self, instruction: Instruction, address: usize) -> Result<(), RegisterError> {
        match self.value {
            Value::R(_) => {
                if address < 11 {
                    let mut m = [None; 11];
                    m[address] = Some(instruction);
                    self.value = Value::IR(m, SplitRegister::new());
                    Ok(())
                } else if address < 24 {
                    let mut m = [None; 24];
                    m[address] = Some(instruction);
                    self.value = Value::I(m);
                    Ok(())
                } else {
                    Err(RegisterError::NoMemory)
                }
            },
            Value::IR(ref mut i, _) => {
                if address < 11 {
                    i[address] = Some(instruction);
                    Ok(())
                } else if address < 24 {
                    let mut m = [None; 24];
                    m[0 .. 11].copy_from_slice(&i[0 .. 11]);
                    m[address] = Some(instruction);
                    self.value = Value::I(m);
                    Ok(())
                } else {
                    Err(RegisterError::NoMemory)
                }
            },
            Value::I(ref mut i) => {
                if address < 24 {
                    i[address] = Some(instruction);
                    Ok(())
                } else {
                    Err(RegisterError::NoMemory)
                }
            },
        }
    }

    /// Supports the dr_exchange instruction
    /// avoiding unnecessary allocations.
    pub fn exchange(&mut self, r: SplitRegister) -> SplitRegister {
        let result = match self.value {
            Value::R(reg) => reg,
            Value::IR(_, reg) => reg,
            _ => SplitRegister::new(),
        };

        self.value = Value::R(r);

        result
    }
}

impl Default for MemoryRegister {
    fn default() -> Self {
        Self::new()
    }
}

impl From<Decimal> for MemoryRegister {
    fn from(n: Decimal) -> Self {
        MemoryRegister {
            value: Value::R(n.into()),
        }
    }
}

impl From<(Decimal, Decimal)> for MemoryRegister {
    fn from(n: (Decimal, Decimal)) -> Self {
        MemoryRegister {
            value: Value::R((n.0, n.1).into()),
        }
    }
}

impl From<[Option<Instruction>; 24]> for MemoryRegister {
    fn from(i: [Option<Instruction>; 24]) -> Self {
        MemoryRegister {
            value: Value::I(i),
        }
    }
}

impl From<(Decimal, [Option<Instruction>; 11])> for MemoryRegister {
    fn from(ri: (Decimal, [Option<Instruction>; 11])) -> Self {
        MemoryRegister {
            value: Value::IR(ri.1, ri.0.into()),
        }
    }
}


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    pub fn register_default_value_is_zero() {
        let r = MemoryRegister::new();

        assert_eq!(Decimal::new(0, 0), r.read());
    }

    #[test]
    pub fn can_write_to_the_register() {
        let mut r = MemoryRegister::new();

        let result = r.write(100.into());

        assert_eq!(Ok(()), result);
        assert_eq!(Decimal::new(100, 0), r.read());
    }

    #[test]
    pub fn can_write_to_the_split_register() {
        let mut r = MemoryRegister::new();

        let result = r.write_left(100.into());

        assert_eq!(Ok(()), result);
        assert_eq!(Decimal::new(100, 0), r.read_left());

        let result = r.write(200.into());

        assert_eq!(Ok(()), result);
        assert_eq!(Decimal::new(200, 0), r.read());
    }

    #[test]
    pub fn can_read_instructions_within_bounds_from_ir_register() {
        let mut i = [None; 11];
        i[0] = Some(Instruction::Abs);
        i[1] = Some(Instruction::NewLine);

        let r = MemoryRegister {
            value: Value::IR(i, SplitRegister::new())
        };

        assert_eq!(Ok(Some(Instruction::Abs)), r.read_instruction(0));
        assert_eq!(Ok(Some(Instruction::NewLine)), r.read_instruction(1));
        assert_eq!(Ok(None), r.read_instruction(2));
        assert_eq!(Ok(None), r.read_instruction(10));
        assert_eq!(Err(RegisterError::NoMemory), r.read_instruction(11));
        assert_eq!(Err(RegisterError::NoMemory), r.read_instruction(23));
        assert_eq!(Err(RegisterError::NoMemory), r.read_instruction(24));
    }

    #[test]
    pub fn can_read_instructions_within_bounds_from_i_register() {
        let mut i = [None; 24];
        i[0] = Some(Instruction::Abs);
        i[1] = Some(Instruction::NewLine);

        let r = MemoryRegister {
            value: Value::I(i)
        };

        assert_eq!(Ok(Some(Instruction::Abs)), r.read_instruction(0));
        assert_eq!(Ok(Some(Instruction::NewLine)), r.read_instruction(1));
        assert_eq!(Ok(None), r.read_instruction(2));
        assert_eq!(Ok(None), r.read_instruction(10));
        assert_eq!(Ok(None), r.read_instruction(11));
        assert_eq!(Ok(None), r.read_instruction(23));
        assert_eq!(Err(RegisterError::NoMemory), r.read_instruction(25));
    }

    #[test]
    pub fn can_write_to_ir_register() {
        let mut r = MemoryRegister {
            value: Value::IR([None; 11], SplitRegister::new())
        };

        assert_eq!(Ok(()), r.write_left(1.into()));
        assert_eq!(Decimal::new(1, 0), r.read_left());

        assert_eq!(Ok(()), r.write_instruction(Instruction::Abs, 0));
        assert_eq!(Ok(Some(Instruction::Abs)), r.read_instruction(0));

        assert_eq!(Ok(()), r.write_instruction(Instruction::NewLine, 1));
        assert_eq!(Ok(Some(Instruction::NewLine)), r.read_instruction(1));

        assert_eq!(Ok(None), r.read_instruction(2));
        assert_eq!(Ok(None), r.read_instruction(3));
        assert_eq!(Ok(None), r.read_instruction(4));
        assert_eq!(Ok(None), r.read_instruction(5));
        assert_eq!(Ok(None), r.read_instruction(6));
        assert_eq!(Ok(None), r.read_instruction(7));
        assert_eq!(Ok(None), r.read_instruction(8));
        assert_eq!(Ok(None), r.read_instruction(9));

        assert_eq!(Ok(()), r.write_instruction(Instruction::Abs, 10));
        assert_eq!(Ok(Some(Instruction::Abs)), r.read_instruction(10));

        assert_eq!(Err(RegisterError::NoMemory), r.read_instruction(11));
    }

    #[test]
    pub fn can_write_to_i_register() {
        let mut r = MemoryRegister {
            value: Value::I([None; 24])
        };

        assert_eq!(Ok(()), r.write_instruction(Instruction::Abs, 0));
        assert_eq!(Ok(Some(Instruction::Abs)), r.read_instruction(0));

        assert_eq!(Ok(()), r.write_instruction(Instruction::NewLine, 1));
        assert_eq!(Ok(Some(Instruction::NewLine)), r.read_instruction(1));

        assert_eq!(Ok(()), r.write_instruction(Instruction::Abs, 10));
        assert_eq!(Ok(Some(Instruction::Abs)), r.read_instruction(10));

        assert_eq!(Ok(()), r.write_instruction(Instruction::NewLine, 23));
        assert_eq!(Ok(Some(Instruction::NewLine)), r.read_instruction(23));

        assert_eq!(Ok(None), r.read_instruction(2));
        assert_eq!(Ok(None), r.read_instruction(3));
        assert_eq!(Ok(None), r.read_instruction(4));
        assert_eq!(Ok(None), r.read_instruction(5));
        assert_eq!(Ok(None), r.read_instruction(6));
        assert_eq!(Ok(None), r.read_instruction(7));
        assert_eq!(Ok(None), r.read_instruction(8));
        assert_eq!(Ok(None), r.read_instruction(9));

        assert_eq!(Ok(None), r.read_instruction(11));
        assert_eq!(Ok(None), r.read_instruction(12));
        assert_eq!(Ok(None), r.read_instruction(13));
        assert_eq!(Ok(None), r.read_instruction(14));
        assert_eq!(Ok(None), r.read_instruction(15));
        assert_eq!(Ok(None), r.read_instruction(16));
        assert_eq!(Ok(None), r.read_instruction(17));
        assert_eq!(Ok(None), r.read_instruction(18));
        assert_eq!(Ok(None), r.read_instruction(19));
        assert_eq!(Ok(None), r.read_instruction(20));
        assert_eq!(Ok(None), r.read_instruction(21));
        assert_eq!(Ok(None), r.read_instruction(22));

        assert_eq!(Err(RegisterError::NoMemory), r.read_instruction(24));
    }
}
