// 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::num::Wrapping;
use std::u64;

use quickcheck::{Arbitrary,Gen};

use {NameRef,Result,SegmentRef};

/// A variable with known size.
#[derive(Clone,Copy,PartialEq,Eq,PartialOrd,Ord,Debug,Hash)]
pub struct Variable {
    /// Name of the variable.
    pub name: NameRef,
    /// Size of the variable value in bits. Never zero.
    pub bits: usize,
}

impl Variable {
    /// Crates a new variable. Fails if `bits` zero.
    pub fn new(name: NameRef, bits: usize) -> Result<Variable> {
        if bits == 0 { return Err("Variable can't have size 0".into()); }

        Ok(Variable{
            name: name,
            bits: bits
        })
    }
}

/// A constant value.
#[derive(Clone,Copy,PartialEq,Eq,PartialOrd,Ord,Debug,Hash)]
pub struct Constant {
    /// Value
    pub value: u64,
    /// Size of the value in bits. Never zero.
    pub bits: usize,
}

impl Constant {
    /// Create a new constant. Fails if `bits` is zero.
    pub fn new(value: u64, bits: usize) -> Result<Constant> {
        if bits == 0 { return Err("Variable can't have size 0".into()); }

        Ok(Constant{
            value: value,
            bits: bits
        })
    }

    /// Returns a bit mask covering all bits of the constant. Returns `u64::MAX` if `bits` > 64.
    pub fn mask(&self) -> Wrapping<u64> {
        Wrapping(if self.bits < 64 { (1u64 << self.bits) - 1 } else { u64::MAX })
    }
}

impl Into<Wrapping<u64>> for Constant {
    fn into(self) -> Wrapping<u64> {
        Wrapping(self.value) & self.mask()
    }
}

/// A RREIL value.
#[derive(Clone,Copy,PartialEq,Eq,PartialOrd,Ord,Debug,Hash)]
pub enum Value {
    /// An undefined value of unknown size.
    Undefined,
    /// A RREIL variable, possibly in SSA form.
    Variable(Variable),
    /// A constant value.
    Constant(Constant),
}

impl Value {
    /// Creates a new constant value. Fails if `bits` is zero.
    pub fn val(val: u64, bits: usize) -> Result<Value> {
        Ok(Value::Constant(Constant::new(val,bits)?))
    }

    /// Creates a new variable. Fails if `bits` is zero.
    pub fn var(name: NameRef, bits: usize) -> Result<Value> {
        Ok(Value::Variable(Variable::new(name,bits)?))
    }

    /// Creates an undefined value.
    pub fn undef() -> Value {
        Value::Undefined
    }

    /// Returns the size of the value in bits or `None` if it is the undefined value.
    pub fn bits(&self) -> Option<usize> {
        match self {
            &Value::Variable(Variable{ bits,.. }) => Some(bits),
            &Value::Constant(Constant{ bits,.. }) => Some(bits),
            &Value::Undefined => None,
        }
    }


}

impl From<Variable> for Value {
    fn from(v: Variable) -> Value {
        Value::Variable(v)
    }
}

impl From<Constant> for Value {
    fn from(v: Constant) -> Value {
        Value::Constant(v)
    }
}

/// A RREIL memory segment identifier.
#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)]
pub struct Segment {
    /// Segment name.
    pub name: SegmentRef,
}

impl Arbitrary for Variable {
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        Variable {
            name: NameRef::arbitrary(g),
            bits: 1 << g.gen_range(0, 11),
        }
    }
}

impl Arbitrary for Constant {
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        Constant{
            value: g.gen(),
            bits: 1 << g.gen_range(0, 11),
        }
    }
}

impl Arbitrary for Segment {
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        Segment{ name: SegmentRef::new(g.gen_range(0,100)) }
    }
}

impl Arbitrary for Value {
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        match g.gen_range(0, 2) {
            0 => Value::Undefined,
            1 => Value::Variable(Variable::arbitrary(g)),
            2 => Value::Constant(Constant::arbitrary(g)),
            _ => unreachable!(),
        }
    }
}
