// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! An owned, task-local, reference counted type
//!
//! # Safety note
//!
//! XXX There is currently no type-system mechanism for enforcing that
//! reference counted types are both allocated on the exchange heap
//! and also non-sendable
//!
//! This doesn't prevent borrowing multiple aliasable mutable pointers

use ops::Drop;
use clone::Clone;
use libc::c_void;
use cast;

pub struct RC<T> {
    p: *c_void // ~(uint, T)
}

impl<T> RC<T> {
    pub fn new(val: T) -> RC<T> {
        unsafe {
            let v = ~(1, val);
            let p: *c_void = cast::transmute(v);
            RC { p: p }
        }
    }

    fn get_mut_state(&mut self) -> *mut (uint, T) {
        unsafe {
            let p: &mut ~(uint, T) = cast::transmute(&mut self.p);
            let p: *mut (uint, T) = &mut **p;
            return p;
        }
    }

    fn get_state(&self) -> *(uint, T) {
        unsafe {
            let p: &~(uint, T) = cast::transmute(&self.p);
            let p: *(uint, T) = &**p;
            return p;
        }
    }

    pub fn unsafe_borrow_mut(&mut self) -> *mut T {
        unsafe {
            match *self.get_mut_state() {
                (_, ref mut p) => {
                    let p: *mut T = p;
                    return p;
                }
            }
        }
    }

    pub fn refcount(&self) -> uint {
        unsafe {
            match *self.get_state() {
                (count, _) => count
            }
        }
    }
}

#[unsafe_destructor]
impl<T> Drop for RC<T> {
    fn drop(&self) {
        assert!(self.refcount() > 0);

        unsafe {
            // FIXME(#4330) Need self by value to get mutability.
            let this: &mut RC<T> = cast::transmute_mut(self);

            match *this.get_mut_state() {
                (ref mut count, _) => {
                    *count = *count - 1
                }
            }

            if this.refcount() == 0 {
                let _: ~(uint, T) = cast::transmute(this.p);
            }
        }
    }
}

impl<T> Clone for RC<T> {
    fn clone(&self) -> RC<T> {
        unsafe {
            // XXX: Mutable clone
            let this: &mut RC<T> = cast::transmute_mut(self);

            match *this.get_mut_state() {
                (ref mut count, _) => {
                    *count = *count + 1;
                }
            }
        }

        RC { p: self.p }
    }
}

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

    #[test]
    fn smoke_test() {
        unsafe {
            let mut v1 = RC::new(100);
            assert!(*v1.unsafe_borrow_mut() == 100);
            assert!(v1.refcount() == 1);

            let mut v2 = v1.clone();
            assert!(*v2.unsafe_borrow_mut() == 100);
            assert!(v2.refcount() == 2);

            *v2.unsafe_borrow_mut() = 200;
            assert!(*v2.unsafe_borrow_mut() == 200);
            assert!(*v1.unsafe_borrow_mut() == 200);

            let v3 = v2.clone();
            assert!(v3.refcount() == 3);
            {
                let _v1 = v1;
                let _v2 = v2;
            }
            assert!(v3.refcount() == 1);
        }
    }
}
