// Copyright 2018 MaidSafe.net limited.
//
// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. Please review the Licences for the specific language governing
// permissions and limitations relating to use of the SAFE Network Software.

use crate::ffi::arrays::XorNameArray;
use crate::ipc::req::permission_set_into_repr_c;
use ffi_utils::callback::CallbackArgs;
use ffi_utils::{vec_from_raw_parts, ReprC};
use safe_nd::MDataPermissionSet;
use serde::{Deserialize, Serialize};
use std::ffi::CString;
use std::os::raw::c_char;

/// Represents a requested set of changes to the permissions of a mutable data.
#[repr(C)]
#[derive(Copy, Clone, Default, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct PermissionSet {
    /// How to modify the read permission.
    pub read: bool,
    /// How to modify the insert permission.
    pub insert: bool,
    /// How to modify the update permission.
    pub update: bool,
    /// How to modify the delete permission.
    pub delete: bool,
    /// How to modify the manage permissions permission.
    pub manage_permissions: bool,
}

impl ReprC for PermissionSet {
    type C = *const Self;
    type Error = ();

    /// Constructs the object from a raw pointer.
    ///
    /// After calling this function, the raw pointer is owned by the resulting object.
    unsafe fn clone_from_repr_c(repr_c: Self::C) -> Result<Self, Self::Error> {
        Ok(*repr_c)
    }
}

impl CallbackArgs for PermissionSet {
    fn default() -> Self {
        permission_set_into_repr_c(MDataPermissionSet::new())
    }
}

/// Represents an application ID in the process of asking permissions.
#[repr(C)]
pub struct AppExchangeInfo {
    /// UTF-8 encoded id.
    pub id: *const c_char,

    /// Reserved by the frontend.
    ///
    /// null if not present.
    pub scope: *const c_char,

    /// UTF-8 encoded application friendly-name.
    pub name: *const c_char,

    /// UTF-8 encoded application provider/vendor (e.g. MaidSafe).
    pub vendor: *const c_char,
}

impl Drop for AppExchangeInfo {
    fn drop(&mut self) {
        unsafe {
            let _ = CString::from_raw(self.id as *mut _);
            if !self.scope.is_null() {
                let _ = CString::from_raw(self.scope as *mut _);
            }
            let _ = CString::from_raw(self.name as *mut _);
            let _ = CString::from_raw(self.vendor as *mut _);
        }
    }
}

/// Represents the set of permissions for a given container
#[repr(C)]
pub struct ContainerPermissions {
    /// The UTF-8 encoded id
    pub cont_name: *const c_char,
    /// The requested permission set
    pub access: PermissionSet,
}

impl Drop for ContainerPermissions {
    fn drop(&mut self) {
        unsafe {
            let _ = CString::from_raw(self.cont_name as *mut _);
        }
    }
}

/// Represents an authorisation request.
#[repr(C)]
pub struct AuthReq {
    /// The application identifier for this request.
    pub app: AppExchangeInfo,
    /// `true` if the app wants dedicated container for itself. `false` otherwise.
    pub app_container: bool,

    /// App has permission to transfer coins on behalf of the user.
    pub app_permission_transfer_coins: bool,

    /// App has permission to perform mutations on behalf of the user.
    pub app_permission_perform_mutations: bool,

    /// App has permission to read balance on behalf of the user.
    pub app_permission_get_balance: bool,

    /// Array of `ContainerPermissions`.
    pub containers: *const ContainerPermissions,

    /// Size of container permissions array.
    pub containers_len: usize,
}

impl Drop for AuthReq {
    fn drop(&mut self) {
        unsafe {
            let _ = vec_from_raw_parts(
                self.containers as *mut ContainerPermissions,
                self.containers_len,
            );
        }
    }
}

/// Containers request.
#[repr(C)]
pub struct ContainersReq {
    /// Exchange info.
    pub app: AppExchangeInfo,
    /// Requested containers.
    pub containers: *const ContainerPermissions,
    /// Size of requested containers array.
    pub containers_len: usize,
}

impl Drop for ContainersReq {
    fn drop(&mut self) {
        unsafe {
            let _ = vec_from_raw_parts(
                self.containers as *mut ContainerPermissions,
                self.containers_len,
            );
        }
    }
}

#[repr(C)]
/// For use in `ShareMDataReq`. Represents a specific `MutableData` that is being shared.
pub struct ShareMData {
    /// The mutable data type.
    pub type_tag: u64,
    /// The mutable data name.
    pub name: XorNameArray,
    /// The permissions being requested.
    pub perms: PermissionSet,
}

#[repr(C)]
/// Represents a request to share mutable data
pub struct ShareMDataRequest {
    /// Info about the app requesting shared access
    pub app: AppExchangeInfo,
    /// List of MD names & type tags and permissions that need to be shared
    pub mdata: *const ShareMData,
    /// Length of the mdata array
    pub mdata_len: usize,
}

impl Drop for ShareMDataRequest {
    fn drop(&mut self) {
        unsafe {
            let _ = vec_from_raw_parts(self.mdata as *mut ShareMData, self.mdata_len);
        }
    }
}
