// Panopticon - A libre program analysis library for machine code
// Copyright (C) 2014-2018  The Panopticon Developers
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use std::usize;

use smallvec::SmallVec;

use {Area,Function,StrRef,Value};

/// Native ISA mnemonic.
#[derive(Clone,Debug,PartialEq,Eq)]
pub struct Mnemonic {
    /// Addresses this mnemonic occupies.
    pub area: Area,
    /// Disassembled opcode.
    pub opcode: StrRef,
    /// Arguments, in and out.
    pub operands: SmallVec<[Value; 3]>,
    /// Number of IL statements used to model its semantics.
    pub statement_count: usize,
}

impl Mnemonic {
    /// Create a new argument and IL-statement less mnemonic.
    pub fn new(a: Area, s: StrRef) -> Mnemonic {
        Mnemonic{
            area: a,
            opcode: s,
            operands: SmallVec::default(),
            statement_count: 0,
        }
    }
}

/*
/// Internal to `Mnemonic`
#[derive(Clone,Debug)]
pub enum Argument {
    /// Internal to `Mnemonic`
    Literal(StrRef),
    /// Internal to `Mnemonic`
    Value {
        /// Internal to `Mnemonic`
        has_sign: bool,
        /// Internal to `Mnemonic`
        value: Value,
    },
    /// Internal to `Mnemonic`
    Pointer {
        /// Internal to `Mnemonic`
        is_code: bool,
        /// Internal to `Mnemonic`
        region: StrRef,
        /// Internal to `Mnemonic`
        address: Value,
    },
}

macro_rules! arg {
    ( { u : $val:expr } $cdr:tt ) => {
        Argument::Value{
            has_sign: false,
            value: ($val).into(),
        }
    }
    ( { s : $val:expr } $cdr:tt ) => {
        Argument::Value{
            has_sign: true,
            value: ($val).into(),
        }
    }
    ( { p : $val:expr : $bank:expr } $cdr:tt ) => {
        Argument::Pointer{
            is_code: false,
            region: ($bank).into(),
            address: ($val).into(),
        }
    }
    ( { c : $val:expr : $bank:expr } $cdr:tt ) => {
        Argument::Pointer{
            is_code: false,
            region: ($bank).into(),
            address: ($val).into(),
        }
    }
    ( ) => {}
}

arg!({ u : Variable::new("test",1,None) } "sss");
arg!({ s : Variable::new("test",1,None) } "sss");

impl Argument {
    /// format := '{' type '}'
    /// type := 'u' ':' value | # unsigned
    ///         's' ':' value | # signed
    ///         'p' ':' value ':' bank |  # data pointer
    ///         'c' ':' value ':' bank |  # code pointer
    /// value := digit+ | xdigit+ | # constant
    ///          alpha alphanum* | # variable
    /// bank := alpha alphanum*
     pub fn parse(mut j: Chars) -> Result<Vec<Argument>> {
        named!(main, tag!("{"
*/

/// Index of a mnemonic in a function. Local to a specific function.
#[derive(Clone,Copy,Debug,PartialOrd,Ord,PartialEq,Eq)]
pub struct MnemonicIndex {
    index: usize
}

impl MnemonicIndex {
    /// Create a new MnemonicIndex for the `i`th mnemonic.
    pub fn new(i: usize) -> MnemonicIndex { MnemonicIndex{ index: i } }

    /// Returns the numeric index of this mnemonic. Can be used for arthimetic.
    pub fn index(&self) -> usize { self.index }
}

/// Iterator over a range of mnemonics
#[derive(Clone)]
pub struct MnemonicIterator<'a> {
    function: &'a Function,
    index: usize,
    max: usize,
}

impl<'a> MnemonicIterator<'a> {
    /// Creates a new iterator over mnemonics `[index, max]`, both inclusive.
    pub fn new(function: &'a Function,index: usize, max: usize) -> MnemonicIterator<'a> {
        MnemonicIterator{
            function: function,
            index: index,
            max: max,
        }
    }
}

impl<'a> Iterator for MnemonicIterator<'a> {
    type Item = (MnemonicIndex,&'a Mnemonic);

    fn next(&mut self) -> Option<(MnemonicIndex,&'a Mnemonic)> {
        if self.index <= self.max {
            let idx = MnemonicIndex::new(self.index);
            let mne = self.function.mnemonic(idx);

            self.index += 1;
            Some((idx,mne))
        } else {
            None
        }
    }
}

impl<'a> ExactSizeIterator for MnemonicIterator<'a> {
    fn len(&self) -> usize {
        self.max - self.index + 1
    }
}

impl<'a> DoubleEndedIterator for MnemonicIterator<'a> {
    fn next_back(&mut self) -> Option<(MnemonicIndex,&'a Mnemonic)> {
        if self.index < self.max {
            let idx = MnemonicIndex::new(self.max - 1);
            let mne = self.function.mnemonic(idx);

            self.max -= 1;
            Some((idx,mne))
        } else {
            None
        }
    }
}
