// 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

//! A Basic Block is a uninterrupted sequence of machine code.

use std::ops::Range;
use std::usize;
use petgraph::graph::NodeIndex;
use {Uuid, Area, Function};
use mnemonic::MnemonicIndex;

/// An uninterrupted sequence of machine code.
/// Basic blocks cover a continuous address range.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BasicBlock {
    /// Fixed UUID of this basic block. Never changes.
    pub uuid: Uuid,
    /// Range of indices into the functions mnmonic list.
    pub mnemonics: Range<MnemonicIndex>,
    /// Node this basic block occupies in the functions control flow graph.
    pub node: NodeIndex,
    /// Bytes covered byte the basic block.
    pub area: Area,
    /// Range of indices into the functions IL statement list.
    pub statements: Range<usize>,
}

impl BasicBlock {
    /// Address range covered.
    pub fn area<'a>(&'a self) -> &'a Area { &self.area }
}

/// Index of the basic block.
#[derive(Clone,Copy,Debug,PartialOrd,Ord,PartialEq,Eq)]
pub struct BasicBlockIndex {
    index: usize
}

impl BasicBlockIndex {
    /// Creates an index for basic block number `i`.
    pub fn new(i: usize) -> BasicBlockIndex { BasicBlockIndex{ index: i } }
    /// Numeric index.
    pub fn index(&self) -> usize { self.index }
}

#[derive(Clone)]
/// Iterator over basic blocks.
pub struct BasicBlockIterator<'a> {
    function: &'a Function,
    index: usize,
    max: usize,
}

impl<'a> BasicBlockIterator<'a> {
    /// Create a new basic block iterator.
    pub fn new(function: &'a Function,index: usize, max: usize) -> BasicBlockIterator<'a> {
        BasicBlockIterator{
            function: function,
            index: index,
            max: max,
        }
    }
}


impl<'a> Iterator for BasicBlockIterator<'a> {
    type Item = (BasicBlockIndex,&'a BasicBlock);

    fn next(&mut self) -> Option<(BasicBlockIndex,&'a BasicBlock)> {
        if self.index < self.max {
            let idx = BasicBlockIndex::new(self.index);
            let bb = self.function.basic_block(idx);

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

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

impl<'a> DoubleEndedIterator for BasicBlockIterator<'a> {
    fn next_back(&mut self) -> Option<(BasicBlockIndex,&'a BasicBlock)> {
        if self.index < self.max {
            let idx = BasicBlockIndex::new(self.max - 1);
            let bb = self.function.basic_block(idx);

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