use std::cell::RefCell;
use std::convert::TryInto;
use std::fmt::Write;

use bitflags::bitflags;
pub use merklehash::MerkleHash;
use merklehash::*;
use serde::{Deserialize, Serialize};
/// A more compact representation of nodes in the MerkleTree.
/// The ID is a counter that begins at 1. 0 is not a valid ID.
pub type MerkleNodeId = u64;

/// Node parents can be unassigned in which case 0 is used to identify that case.
/// 0 is not a valid node ID.
pub const ID_UNASSIGNED: MerkleNodeId = 0;

/**
 * Nodes can have one or more sources of data.
 * Either from a FILE, CAS or both. This enumeration is used for several
 * NodeAttribute accessors.
 */
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum NodeDataType {
    CAS = 0,
    FILE = 1,
}
bitflags! {

    /**
     * Nodes can have one or more sources of data.
     * Either from a FILE, CAS or both. DataTypeBitfield provides
     * a bitfield type for use internally.
     */
    #[derive(Serialize, Deserialize, Default)]
    struct DataTypeBitfield: u8 {
        /// This node contains a Cas entry
        const CAS = 0x1_u8;
        /// This node contains a File entry
        const FILE = 0x2_u8;
    }
}

/************************************************************************* */
/*  */
/* MerkleNode */
/*  */
/************************************************************************* */

/**
 * Represents an immutable node in a MerkleDB. This node once constructed
 * and stored is completely immutable.
 */
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct MerkleNode {
    /// The Id of the current node.
    id: MerkleNodeId,
    /// The Merkle Hash of the current node. We don't serialize this
    hash: MerkleHash,
    /// The length of the bytes stored here
    len: usize,
    /// The IDs of the children
    children: Vec<(MerkleNodeId, usize)>,
}

impl Default for MerkleNode {
    fn default() -> MerkleNode {
        MerkleNode {
            id: ID_UNASSIGNED,
            hash: MerkleHash::default(),
            len: 0,
            children: Vec::new(),
        }
    }
}

impl MerkleNode {
    /// Gets the id of this node
    pub fn id(&self) -> MerkleNodeId {
        self.id
    }
    /// Gets the hash of this node
    pub fn hash(&self) -> &MerkleHash {
        &self.hash
    }
    /// Gets the length of data this node represents
    pub fn len(&self) -> usize {
        self.len
    }
    /// Whether the node contains empty data
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }
    /// Gets the list of IDs of the children and their lengths
    pub fn children(&self) -> &Vec<(MerkleNodeId, usize)> {
        &self.children
    }
    /// Updates the list of children.
    pub fn set_children(&mut self, ch: Vec<(MerkleNodeId, usize)>) {
        self.children = ch;
    }
    /// Constructs a new node
    pub fn new(id: MerkleNodeId, hash: MerkleHash, len: usize, children: Vec<(MerkleNodeId, usize)>) -> MerkleNode {
        MerkleNode {
            id,
            hash,
            len,
            children,
        }
    }
}

thread_local! {
static HASH_NODE_SEQUENCE_BUFFER: RefCell<String> =
    RefCell::new(String::with_capacity(1024));
}

/**
 * Hashing method used to derive a parent node from child node hashes.
 *
 * We just print out the string
 *
 * ```ignore
 * [child hash 1] : [child len 1]
 * [child hash 2] : [child len 2]
 * [child hash 3] : [child len 2]
 * ```
 *
 * With a "\n" after every line. And hash that.
 *
 * For performance, this function reuses an internal thread-local buffer.
 */
pub fn hash_node_sequence(hash: &[MerkleNode]) -> MerkleHash {
    HASH_NODE_SEQUENCE_BUFFER.with(|buffer| {
        let mut buf = buffer.borrow_mut();
        buf.clear();
        for node in hash.iter() {
            writeln!(buf, "{:x} : {}", node.hash(), node.len()).unwrap();
        }
        compute_internal_node_hash(buf.as_bytes())
    })
}

/************************************************************************* */
/*  */
/* MerkleNodeAttributes */
/*  */
/************************************************************************* */

/**
 * Each node may have additional attributes to identify where we can go
 * to find the value of a node. For instance, the node may by itself reference
 * an entry in CAS storage, or a File. This can be checked by inspecting the
 * attributes bitfield. Alternatively, it may be a substring / descendent of
 * a File, and the originating File can be found by following the parent[File]
 * link.
 */
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default)]
pub struct MerkleNodeAttributes {
    /**
     * If parent[CAS] is set, the value of this node can be found by
     * traversing to parent[CAS]. parent[CAS] must have this node as a child.
     */
    parent: [MerkleNodeId; 2],
    /**
     * If attributes[CAS] is set, then the value of this node can be found
     * by querying CAS storage. Similarly, if attributes[FILE] is set
     * then there exists a file whose contents are exactly this node.
     */
    attributes: DataTypeBitfield,
}

impl MerkleNodeAttributes {
    /// Returns the parent of this node in the CAS graph.
    pub fn cas_parent(&self) -> MerkleNodeId {
        self.parent[NodeDataType::CAS as usize]
    }
    /// Returns the parent of this node in the FILE graph.
    pub fn file_parent(&self) -> MerkleNodeId {
        self.parent[NodeDataType::FILE as usize]
    }
    /** Sets parent of this node in the CAS graph.
     * Unchecked Invariant: parent must have this node as a child.
     */
    pub fn set_cas_parent(&mut self, parent: MerkleNodeId) {
        self.parent[NodeDataType::CAS as usize] = parent;
    }
    /** Sets parent of this node in the FILE graph.
     * Unchecked Invariant: parent must have this node as a child.
     */
    pub fn set_file_parent(&mut self, parent: MerkleNodeId) {
        self.parent[NodeDataType::FILE as usize] = parent;
    }
    /** Sets parent of this node in either FILE or CAS graph.
     * Unchecked Invariant: parent must have this node as a child.
     */
    pub fn set_parent(&mut self, parent_type: NodeDataType, parent: MerkleNodeId) {
        self.parent[parent_type as usize] = parent;
    }
    /**
     * Returns the parent of this node in either graph.
     */
    pub fn parent(&self, parent_type: NodeDataType) -> MerkleNodeId {
        self.parent[parent_type as usize]
    }
    /// Whether this node is a root of a file
    pub fn is_file(&self) -> bool {
        self.attributes.contains(DataTypeBitfield::FILE)
    }
    /// Whether this node points to a complete entry in Content Addressed Storage
    pub fn is_cas(&self) -> bool {
        self.attributes.contains(DataTypeBitfield::CAS)
    }
    /// Whether this node points to a complete entry in either FILE or CAS
    pub fn is_type(&self, data_type: NodeDataType) -> bool {
        if data_type == NodeDataType::CAS {
            self.is_cas()
        } else {
            self.is_file()
        }
    }

    /// Whether this node is a root of a file
    pub fn set_file(&mut self) {
        self.attributes.set(DataTypeBitfield::FILE, true);
    }
    /// Whether this node points to a complete entry in Content Addressed Storage
    pub fn set_cas(&mut self) {
        self.attributes.set(DataTypeBitfield::CAS, true);
    }

    /// Returns true if both self and other have the same attributes (FILE or CAS)
    pub fn type_equal(&self, other: &MerkleNodeAttributes) -> bool {
        self.attributes == other.attributes
    }
    /** Whether this node is a substring of a CAS entry.
     * This is a simple check: either this node is a CAS entry, or
     * it has a CAS parent.
     */
    pub fn has_cas_data(&self) -> bool {
        self.attributes.contains(DataTypeBitfield::CAS) || self.parent[NodeDataType::CAS as usize] != ID_UNASSIGNED
    }
    /** Whether this node is a substring of a FILE entry.
     * This is a simple check: either this node is a FILE entry, or
     * it has a CAS parent.
     */
    pub fn has_file_data(&self) -> bool {
        self.attributes.contains(DataTypeBitfield::FILE) || self.parent[NodeDataType::FILE as usize] != ID_UNASSIGNED
    }
    /**
     * Checks if this is a substring of either a CAS or FILE entry.
     */
    pub fn has_data(&self, data_type: NodeDataType) -> bool {
        if data_type == NodeDataType::CAS {
            self.has_cas_data()
        } else {
            self.has_file_data()
        }
    }
    pub fn merge_attributes(&mut self, other: &MerkleNodeAttributes) {
        self.attributes |= other.attributes;
    }
}
/************************************************************************* */
/*  */
/* ObjectRange */
/*  */
/************************************************************************* */

/**
 * Describes a range of bytes in an object.
 */
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct ObjectRange {
    pub hash: MerkleHash,
    pub start: usize,
    pub end: usize,
}

/**
 * Describes a range of bytes in an object.
 */
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct ObjectRangeById {
    pub id: MerkleNodeId,
    pub start: usize,
    pub end: usize,
}

/**
 * Given a collection of ranges, simplify the ranges by collapsing
 * consesutive ranges into one. For instance:
 *
 * ```ignore
 * [a, 0, 10], [a,10,20], [b,0, 15]
 * ```
 *
 * will be collapsed into
 *
 * ```ignore
 * [a, 0, 20], [b,0, 15]
 * ```
 */
pub fn simplify_ranges(ranges: &[ObjectRange]) -> Vec<ObjectRange> {
    let mut ret: Vec<ObjectRange> = Vec::with_capacity(ranges.len());
    for range in ranges {
        if !ret.is_empty() {
            let last = ret.last_mut().unwrap();
            if last.hash == range.hash && last.end == range.start {
                last.end = range.end;
                continue;
            }
        }
        ret.push(range.clone());
    }
    ret
}

/************************************************************************* */
/*  */
/* Conversion to and from bytes for the RocksDB storage */
/*  */
/************************************************************************* */

pub trait RocksDBConversion<T> {
    // TODO: can we do better than Vec<u8> here? This incurs a small heap alloc
    // especially for NodeId and MerkleHash that should be unnecessary
    // Perhaps we can template around it?
    fn to_db_bytes(&self) -> Vec<u8>;
    fn from_db_bytes(bytes: &[u8]) -> T;
}
/**
 * Conversion routines to and from bytes
 */
impl RocksDBConversion<MerkleNode> for MerkleNode {
    fn to_db_bytes(&self) -> Vec<u8> {
        bincode::serialize(self).unwrap()
    }
    fn from_db_bytes(bytes: &[u8]) -> MerkleNode {
        bincode::deserialize(bytes).unwrap()
    }
}

/**
 * Conversion routines to and from bytes for MerkleNodeId.
 * We use Big Endian here so the lexicographic sort by RocksDB
 * gives the nodes in the right order
 */
impl RocksDBConversion<MerkleNodeId> for MerkleNodeId {
    fn to_db_bytes(&self) -> Vec<u8> {
        self.to_be_bytes().to_vec()
    }
    fn from_db_bytes(bytes: &[u8]) -> MerkleNodeId {
        MerkleNodeId::from_be_bytes(bytes.try_into().unwrap())
    }
}

impl RocksDBConversion<MerkleHash> for MerkleHash {
    fn to_db_bytes(&self) -> Vec<u8> {
        self.as_bytes().to_vec()
    }
    fn from_db_bytes(bytes: &[u8]) -> MerkleHash {
        MerkleHash::try_from(bytes).unwrap()
    }
}
