pub use p101_is::instruction;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Message {
    /// Tell all components to stop
    Halt,
    
    /// Print the value of the selected register to the output device
    Print(rust_decimal::Decimal, p101_is::Register, Operator),

    /// Print an empty line to the output device
    NewLine,

    /// A card has been inserted in the reader.
    /// This event is user-initiated.
    CardInserted(Card),

    /// The card has been removed from the reader
    /// This event is user-initiated.
    CardRemoved,

    /// Read the program from the card in the reader
    ReadTextFromCard, 

    /// A program has been loaded
    TextRead(String),

    /// The program must be saved on the card in the reader.
    /// The program is in the encoded form: no transformation
    /// are done before saving it.
    StoreTextOnCard(String),

    /// Precision to show on the GUI
    SetPrecision(u8),

    /// Status of the record program switch
    RecordProgramSwitch(bool),

    /// Status of the print program switch
    PrintProgramSwitch(bool),

    /// Global system status (off, on, ready)
    SystemStatusChanged(SystemStatus),

    /// An error occurred
    Error,

    /// A key has been pressed
    KeyPressed(Key),

    /// The ALU waits for user input
    ProgramStopped,

    /// Program execution is ended and all components have to shutdown
    ProgramEnded,

    /// Program execution has just started
    ProgramStarted,

    Debug(DebugEvent)
}

#[derive(PartialEq, Hash, Clone, Debug, Eq)]
pub enum MessageType {
    CardInserted = 1,
    CardRemoved = 2,
    Error = 3,
    Halt = 4,
    KeyPressed = 5,
    SystemStatusChanged = 7,
    Print = 8,
    NewLine = 9,
    PrintProgramSwitch = 10,
    ProgramStarted = 11,
    ProgramEnded = 12,
    ProgramStopped = 13,
    ReadTextFromCard = 14, 
    RecordProgramSwitch = 15,
    SetPrecision = 16,
    StoreTextOnCard = 17,
    TextRead = 18,
    Debug = 19
}

impl From<&Message> for MessageType {
    fn from(message: &Message) -> Self { 
        match message {
            Message::CardInserted(_) => MessageType::CardInserted,
            Message::CardRemoved => MessageType::CardRemoved,
            Message::Error => MessageType::Error,
            Message::Halt => MessageType::Halt,
            Message::KeyPressed(_) => MessageType::KeyPressed,
            Message::Print(_, _, _) => MessageType::Print,
            Message::NewLine => MessageType::NewLine,
            Message::PrintProgramSwitch(_) => MessageType::PrintProgramSwitch,
            Message::SystemStatusChanged(_) => MessageType::SystemStatusChanged,
            Message::ProgramStarted => MessageType::ProgramStarted,
            Message::ProgramEnded => MessageType::ProgramEnded,
            Message::ProgramStopped => MessageType::ProgramStopped,
            Message::RecordProgramSwitch(_) => MessageType::RecordProgramSwitch,
            Message::ReadTextFromCard => MessageType::ReadTextFromCard, 
            Message::SetPrecision(_) => MessageType::SetPrecision,
            Message::StoreTextOnCard(_) => MessageType::StoreTextOnCard,
            Message::TextRead(_) => MessageType::TextRead,
            Message::Debug(_) => MessageType::Debug
        }
    }
}


#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub enum SystemStatus {
    /// Machine turned of
    Off,

    /// Machine powered on in initial random state
    On,

    /// Fully operative machine
    Ready
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Operator {
    Add,
    Sub,
    Mul,
    Div,
    Sqr,
    CopyM,
    CopyToA,
    SwapA,
    Print,
    Reset
}


#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DebugEvent {
    /// A register has been updated. The event convyes the new value.
    /// This event may be used, for instance, to display the register
    /// value on the printer or in a memory inspector.
    RegisterUpdated(p101_is::Register, rust_decimal::Decimal),
    
    /// A register has been selected by the user. This event may be used
    /// to render the selected register to ease computer usage.
    RegisterSelected(p101_is::Register),

    /// The instruction pointer now refer the address conveyed by the event.
    InstructionPointerUpdated(usize),

    /// The memory location at the convyed address has a new value.
    MemoryUpdated(usize, String)
}

/// Card is a physical support used to store and read data.
/// Different kinds on cards are available for differen uses
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Card {
    /// The memory card is a card backed up by ram.
    MemoryCard { text: String },

    /// This card saves its data to a file.
    FileCard { path: String }
}

impl Card {
    /// Creates a new memory card and initialised it
    /// with the bytes parameter.
    pub fn new_memory_card(text: &str) -> Card {
        Card::MemoryCard {
            text: String::from(text)
        }
    }

    /// Creates a new file card using the file
    /// which path is passed as parameter.
    pub fn new_file_card(path: &str) -> Card {
        Card::FileCard {
            path: String::from(path)
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Key {
    /// Digits
    One,
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Zero,
    Comma,
    Negative,

    /// Available routines
    V,
    W,
    Y,
    Z,

    /// The ON-OFF KEY is a dual purpose switch for both the ON and 
    /// OFF positions. Note: the OFF position automatically clears
    /// all stored data and instructions.
    Power,

    /// The GENERAL RESET KEY erases all data and instructions 
    /// from the computer and turns off the error light.
    GeneralReset,

    /// The DECIMAL WHEEL determines the number of decimal places
    /// (0-15) to which computations will be carried out in the A
    /// register and the decimal places in the printed output,
    /// except for results from the R register.
    DecimalUp,
    DecimalDown,

    /// The RECORD PROGRAM SWITCH, when ON (in), directs the computer
    /// to store instructions either in the memory from the keyboard,
    /// or onto a magnetic program card from the memory.
    /// The RECORD PROGRAM SWITCH must be OFF (out) to load 
    /// instructions from a magnetic program card into the memory.
    RecordProgram,

    /// The PRINT PROGRAM SWITCH, when ON (in), directs the computer
    /// to print out the instructions stored in memory from its
    /// present location in the program to the next Stop 
    /// instruction, whenever the Print key (20) is depressed.
    PrintProgram,
    
    /// TAPE ADVANCE advances the paper tape.
    TapeAdvance,

    /// TAPE RELEASE LEVER enables precise finger-tip adjustment
    /// when changing tape rolls.
    TapeRelease,

    A,
    B,
    C,
    D,
    E,
    F,
    R,

    Add,
    Subtract,
    Multiply,
    Divide,
    SquareRoot,

    /// The "copy to A" or simply "to A" operation.  It is the ↓ 
    /// (down arrow) button. An instruction containing this operatio
    /// directs the computer to transfer the contents of the 
    /// addressed register to A while retaining them in the original
    /// register. The contents of M and R are not affected. 
    /// The previous contents of A are destroyed. The setting of 
    /// the Decimal Wheel has no effect on this operation.
    CopyToA, 

    /// The "copy M" or "From M" operation.  It is the ↑ (up arrow)
    /// button. An instruction containing this operation directs the
    /// computer to transfer the contents of M to the addressed 
    /// register while retaining them in M. The contents of 
    /// registers A and R are unaffected by this instruction. 
    /// The original contents of the addressed register are 
    /// destroyed. The setting of the Decimal Wheel has no effect 
    /// on this operation.
    CopyM,

    /// The CLEAR ENTRY KEY clears the entire keyboard entry. When
    /// keying in a program, a depression of the clear key will erase
    /// the last instruction that has been entered.
    ClearEntry,
    
    /// The CLEAR KEY clears the contents of an addressed
    /// register. When the computer is operated manually, a
    /// depression of this key will print the number in that
    /// register and clear it.    
    Clear,

    /// The SPLIT KEY combined with a register (e.g. C/)
    /// divides that register into two equal parts. When storage
    /// registers are split, the right portion of the split register
    /// retains the original designation while the left side is
    /// identified on the tape with the corresponding lower case
    /// letter (e.g. C/ = c).
    Split,
    
    /// Is it the S button which maps to the Start/Stop instruction.
    Start,   
    
    /// It is the ↕ (up down arrow) button. The EXCHANGE instruction
    /// on the manual. It directs the computer to exchange the
    /// contents of the A register with the contents of the
    /// addressed register. The contents of M are not affected
    /// except by the exchange between A and M. The contents of
    /// the R register are not affected. The setting of the Decimal 
    /// Wheel has no effect on this operation.
    SwapA,

    /// The print key prints the value of the addressed register.
    Print
}

