use std::fs;
use std::mem::{size_of, size_of_val};
use std::os::unix::fs::MetadataExt;
use bitflags::bitflags;
use enum_primitive::*;
pub const P92000: &str = "9P2000";
pub const P92000L: &str = "9P2000.L";
pub const VERSION_UNKNOWN: &str = "unknown";
pub const NOTAG: u16 = !0;
pub const NOFID: u32 = !0;
pub const NONUNAME: u32 = !0;
pub const IOHDRSZ: u32 = 24;
pub const READDIRHDRSZ: u32 = 24;
pub const V9FS_PORT: u16 = 564;
pub mod p92000 {
    
    
    
    pub mod om {
        
        pub const READ: u8 = 0;
        
        pub const WRITE: u8 = 1;
        
        pub const RDWR: u8 = 2;
        
        pub const EXEC: u8 = 3;
        
        pub const TRUNC: u8 = 16;
        
        pub const CEXEC: u8 = 32;
        
        pub const RCLOSE: u8 = 64;
    }
    
    pub mod dm {
        
        pub const DIR: u32 = 0x80000000;
        
        pub const APPEND: u32 = 0x40000000;
        
        pub const EXCL: u32 = 0x20000000;
        
        pub const MOUNT: u32 = 0x10000000;
        
        pub const AUTH: u32 = 0x08000000;
        
        pub const TMP: u32 = 0x04000000;
        
        pub const READ: u32 = 0x4;
        
        pub const WRITE: u32 = 0x2;
        
        pub const EXEC: u32 = 0x1;
    }
    
    
    
    #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
    pub struct Stat {
        
        pub typ: u16,
        
        pub dev: u32,
        
        pub qid: super::Qid,
        
        pub mode: u32,
        
        pub atime: u32,
        
        pub mtime: u32,
        
        pub length: u64,
        
        pub name: String,
        
        pub uid: String,
        
        pub gid: String,
        
        pub muid: String,
    }
    impl Stat {
        
        pub fn size(&self) -> u16 {
            use std::mem::{size_of, size_of_val};
            (size_of_val(&self.typ)
                + size_of_val(&self.dev)
                + size_of_val(&self.qid)
                + size_of_val(&self.mode)
                + size_of_val(&self.atime)
                + size_of_val(&self.mtime)
                + size_of_val(&self.length)
                + (size_of::<u16>() * 4)
                + self.name.len()
                + self.uid.len()
                + self.gid.len()
                + self.muid.len()) as u16
        }
    }
}
bitflags! {
    
    pub struct LockType: u8 {
        const RDLOCK    = 0;
        const WRLOCK    = 1;
        const UNLOCK    = 2;
    }
}
bitflags! {
    
    pub struct LockFlag: u32 {
        #[doc = "Blocking request"]
        const BLOCK     = 1;
        #[doc = "Reserved for future use"]
        const RECLAIM   = 2;
    }
}
bitflags! {
    
    pub struct LockStatus: u8 {
        const SUCCESS   = 0;
        const BLOCKED   = 1;
        const ERROR     = 2;
        const GRACE     = 3;
    }
}
bitflags! {
    
    
    
    
    
    
    #[derive(Default)]
    pub struct QidType: u8 {
        #[doc = "Type bit for directories"]
        const DIR       = 0x80;
        #[doc = "Type bit for append only files"]
        const APPEND    = 0x40;
        #[doc = "Type bit for exclusive use files"]
        const EXCL      = 0x20;
        #[doc = "Type bit for mounted channel"]
        const MOUNT     = 0x10;
        #[doc = "Type bit for authentication file"]
        const AUTH      = 0x08;
        #[doc = "Type bit for not-backed-up file"]
        const TMP       = 0x04;
        #[doc = "Type bits for symbolic links (9P2000.u)"]
        const SYMLINK   = 0x02;
        #[doc = "Type bits for hard-link (9P2000.u)"]
        const LINK      = 0x01;
        #[doc = "Plain file"]
        const FILE      = 0x00;
    }
}
impl From<::std::fs::FileType> for QidType {
    fn from(typ: ::std::fs::FileType) -> Self {
        From::from(&typ)
    }
}
impl<'a> From<&'a ::std::fs::FileType> for QidType {
    fn from(typ: &'a ::std::fs::FileType) -> Self {
        let mut qid_type = QidType::FILE;
        if typ.is_dir() {
            qid_type.insert(QidType::DIR)
        }
        if typ.is_symlink() {
            qid_type.insert(QidType::SYMLINK)
        }
        qid_type
    }
}
bitflags! {
    
    
    
    
    pub struct GetattrMask: u64 {
        const MODE          = 0x00000001;
        const NLINK         = 0x00000002;
        const UID           = 0x00000004;
        const GID           = 0x00000008;
        const RDEV          = 0x00000010;
        const ATIME         = 0x00000020;
        const MTIME         = 0x00000040;
        const CTIME         = 0x00000080;
        const INO           = 0x00000100;
        const SIZE          = 0x00000200;
        const BLOCKS        = 0x00000400;
        const BTIME         = 0x00000800;
        const GEN           = 0x00001000;
        const DATA_VERSION  = 0x00002000;
        #[doc = "Mask for fields up to BLOCKS"]
        const BASIC         =0x000007ff;
        #[doc = "Mask for All fields above"]
        const ALL           = 0x00003fff;
    }
}
bitflags! {
    
    
    
    
    
    
    
    pub struct SetattrMask: u32 {
        const MODE      = 0x00000001;
        const UID       = 0x00000002;
        const GID       = 0x00000004;
        const SIZE      = 0x00000008;
        const ATIME     = 0x00000010;
        const MTIME     = 0x00000020;
        const CTIME     = 0x00000040;
        const ATIME_SET = 0x00000080;
        const MTIME_SET = 0x00000100;
    }
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Qid {
    
    pub typ: QidType,
    
    pub version: u32,
    
    pub path: u64,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Statfs {
    
    pub typ: u32,
    
    pub bsize: u32,
    
    pub blocks: u64,
    
    pub bfree: u64,
    
    pub bavail: u64,
    
    pub files: u64,
    
    pub ffree: u64,
    
    pub fsid: u64,
    
    pub namelen: u32,
}
impl From<nix::sys::statvfs::Statvfs> for Statfs {
    fn from(buf: nix::sys::statvfs::Statvfs) -> Statfs {
        Statfs {
            typ: 0,
            bsize: buf.block_size() as u32,
            blocks: buf.blocks(),
            bfree: buf.blocks_free(),
            bavail: buf.blocks_available(),
            files: buf.files(),
            ffree: buf.files_free(),
            fsid: buf.filesystem_id() as u64,
            namelen: buf.name_max() as u32,
        }
    }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Time {
    pub sec: u64,
    pub nsec: u64,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Stat {
    
    pub mode: u32,
    
    pub uid: u32,
    
    pub gid: u32,
    
    pub nlink: u64,
    
    pub rdev: u64,
    
    pub size: u64,
    
    pub blksize: u64,
    
    pub blocks: u64,
    
    pub atime: Time,
    
    pub mtime: Time,
    
    pub ctime: Time,
}
impl From<fs::Metadata> for Stat {
    fn from(attr: fs::Metadata) -> Self {
        From::from(&attr)
    }
}
impl<'a> From<&'a fs::Metadata> for Stat {
    fn from(attr: &'a fs::Metadata) -> Self {
        Stat {
            mode: attr.mode(),
            uid: attr.uid(),
            gid: attr.gid(),
            nlink: attr.nlink(),
            rdev: attr.rdev(),
            size: attr.size() as u64,
            blksize: attr.blksize() as u64,
            blocks: attr.blocks() as u64,
            atime: Time {
                sec: attr.atime() as u64,
                nsec: attr.atime_nsec() as u64,
            },
            mtime: Time {
                sec: attr.mtime() as u64,
                nsec: attr.mtime_nsec() as u64,
            },
            ctime: Time {
                sec: attr.ctime() as u64,
                nsec: attr.ctime_nsec() as u64,
            },
        }
    }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SetAttr {
    pub mode: u32,
    pub uid: u32,
    pub gid: u32,
    pub size: u64,
    pub atime: Time,
    pub mtime: Time,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DirEntry {
    
    pub qid: Qid,
    
    pub offset: u64,
    
    
    
    pub typ: u8,
    
    pub name: String,
}
impl DirEntry {
    pub fn size(&self) -> u32 {
        (size_of_val(&self.qid)
            + size_of_val(&self.offset)
            + size_of_val(&self.typ)
            + size_of::<u16>()
            + self.name.len()) as u32
    }
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DirEntryData {
    pub data: Vec<DirEntry>,
}
impl DirEntryData {
    pub fn new() -> DirEntryData {
        Self::with(Vec::new())
    }
    pub fn with(v: Vec<DirEntry>) -> DirEntryData {
        DirEntryData { data: v }
    }
    pub fn data(&self) -> &[DirEntry] {
        &self.data
    }
    pub fn size(&self) -> u32 {
        self.data.iter().fold(0, |a, e| a + e.size()) as u32
    }
    pub fn push(&mut self, entry: DirEntry) {
        self.data.push(entry);
    }
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Data(pub Vec<u8>);
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Flock {
    pub typ: LockType,
    pub flags: LockFlag,
    pub start: u64,
    pub length: u64,
    pub proc_id: u32,
    pub client_id: String,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Getlock {
    pub typ: LockType,
    pub start: u64,
    pub length: u64,
    pub proc_id: u32,
    pub client_id: String,
}
enum_from_primitive! {
    #[doc = "Message type, 9P operations"]
    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
    pub enum MsgType {
        
        Tlerror         = 6,    
        Rlerror,
        Tstatfs         = 8,
        Rstatfs,
        Tlopen          = 12,
        Rlopen,
        Tlcreate        = 14,
        Rlcreate,
        Tsymlink        = 16,
        Rsymlink,
        Tmknod          = 18,
        Rmknod,
        Trename         = 20,
        Rrename,
        Treadlink       = 22,
        Rreadlink,
        Tgetattr        = 24,
        Rgetattr,
        Tsetattr        = 26,
        Rsetattr,
        Txattrwalk      = 30,
        Rxattrwalk,
        Txattrcreate    = 32,
        Rxattrcreate,
        Treaddir        = 40,
        Rreaddir,
        Tfsync          = 50,
        Rfsync,
        Tlock           = 52,
        Rlock,
        Tgetlock        = 54,
        Rgetlock,
        Tlink           = 70,
        Rlink,
        Tmkdir          = 72,
        Rmkdir,
        Trenameat       = 74,
        Rrenameat,
        Tunlinkat       = 76,
        Runlinkat,
        
        Tversion        = 100,
        Rversion,
        Tauth           = 102,
        Rauth,
        Tattach         = 104,
        Rattach,
        
        
        Tflush          = 108,
        Rflush,
        Twalk           = 110,
        Rwalk,
        
        
        
        
        Tread           = 116,
        Rread,
        Twrite          = 118,
        Rwrite,
        Tclunk          = 120,
        Rclunk,
        Tremove         = 122,
        Rremove,
        
        
        
        
    }
}
impl MsgType {
    
    pub fn is_t(&self) -> bool {
        !self.is_r()
    }
    
    pub fn is_r(&self) -> bool {
        use crate::MsgType::*;
        match *self {
            Rlerror | Rstatfs | Rlopen | Rlcreate | Rsymlink | Rmknod | Rrename | Rreadlink
            | Rgetattr | Rsetattr | Rxattrwalk | Rxattrcreate | Rreaddir | Rfsync | Rlock
            | Rgetlock | Rlink | Rmkdir | Rrenameat | Runlinkat | Rversion | Rauth | Rattach
            | Rflush | Rwalk | Rread | Rwrite | Rclunk | Rremove => true,
            _ => false,
        }
    }
}
impl<'a> From<&'a Fcall> for MsgType {
    fn from(fcall: &'a Fcall) -> MsgType {
        match *fcall {
            Fcall::Rlerror { .. } => MsgType::Rlerror,
            Fcall::Tstatfs { .. } => MsgType::Tstatfs,
            Fcall::Rstatfs { .. } => MsgType::Rstatfs,
            Fcall::Tlopen { .. } => MsgType::Tlopen,
            Fcall::Rlopen { .. } => MsgType::Rlopen,
            Fcall::Tlcreate { .. } => MsgType::Tlcreate,
            Fcall::Rlcreate { .. } => MsgType::Rlcreate,
            Fcall::Tsymlink { .. } => MsgType::Tsymlink,
            Fcall::Rsymlink { .. } => MsgType::Rsymlink,
            Fcall::Tmknod { .. } => MsgType::Tmknod,
            Fcall::Rmknod { .. } => MsgType::Rmknod,
            Fcall::Trename { .. } => MsgType::Trename,
            Fcall::Rrename => MsgType::Rrename,
            Fcall::Treadlink { .. } => MsgType::Treadlink,
            Fcall::Rreadlink { .. } => MsgType::Rreadlink,
            Fcall::Tgetattr { .. } => MsgType::Tgetattr,
            Fcall::Rgetattr { .. } => MsgType::Rgetattr,
            Fcall::Tsetattr { .. } => MsgType::Tsetattr,
            Fcall::Rsetattr => MsgType::Rsetattr,
            Fcall::Txattrwalk { .. } => MsgType::Txattrwalk,
            Fcall::Rxattrwalk { .. } => MsgType::Rxattrwalk,
            Fcall::Txattrcreate { .. } => MsgType::Txattrcreate,
            Fcall::Rxattrcreate => MsgType::Rxattrcreate,
            Fcall::Treaddir { .. } => MsgType::Treaddir,
            Fcall::Rreaddir { .. } => MsgType::Rreaddir,
            Fcall::Tfsync { .. } => MsgType::Tfsync,
            Fcall::Rfsync => MsgType::Rfsync,
            Fcall::Tlock { .. } => MsgType::Tlock,
            Fcall::Rlock { .. } => MsgType::Rlock,
            Fcall::Tgetlock { .. } => MsgType::Tgetlock,
            Fcall::Rgetlock { .. } => MsgType::Rgetlock,
            Fcall::Tlink { .. } => MsgType::Tlink,
            Fcall::Rlink => MsgType::Rlink,
            Fcall::Tmkdir { .. } => MsgType::Tmkdir,
            Fcall::Rmkdir { .. } => MsgType::Rmkdir,
            Fcall::Trenameat { .. } => MsgType::Trenameat,
            Fcall::Rrenameat => MsgType::Rrenameat,
            Fcall::Tunlinkat { .. } => MsgType::Tunlinkat,
            Fcall::Runlinkat => MsgType::Runlinkat,
            Fcall::Tauth { .. } => MsgType::Tauth,
            Fcall::Rauth { .. } => MsgType::Rauth,
            Fcall::Tattach { .. } => MsgType::Tattach,
            Fcall::Rattach { .. } => MsgType::Rattach,
            Fcall::Tversion { .. } => MsgType::Tversion,
            Fcall::Rversion { .. } => MsgType::Rversion,
            Fcall::Tflush { .. } => MsgType::Tflush,
            Fcall::Rflush => MsgType::Rflush,
            Fcall::Twalk { .. } => MsgType::Twalk,
            Fcall::Rwalk { .. } => MsgType::Rwalk,
            Fcall::Tread { .. } => MsgType::Tread,
            Fcall::Rread { .. } => MsgType::Rread,
            Fcall::Twrite { .. } => MsgType::Twrite,
            Fcall::Rwrite { .. } => MsgType::Rwrite,
            Fcall::Tclunk { .. } => MsgType::Tclunk,
            Fcall::Rclunk => MsgType::Rclunk,
            Fcall::Tremove { .. } => MsgType::Tremove,
            Fcall::Rremove => MsgType::Rremove,
        }
    }
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Fcall {
    
    Rlerror {
        ecode: u32,
    },
    Tstatfs {
        fid: u32,
    },
    Rstatfs {
        statfs: Statfs,
    },
    Tlopen {
        fid: u32,
        flags: u32,
    },
    Rlopen {
        qid: Qid,
        iounit: u32,
    },
    Tlcreate {
        fid: u32,
        name: String,
        flags: u32,
        mode: u32,
        gid: u32,
    },
    Rlcreate {
        qid: Qid,
        iounit: u32,
    },
    Tsymlink {
        fid: u32,
        name: String,
        symtgt: String,
        gid: u32,
    },
    Rsymlink {
        qid: Qid,
    },
    Tmknod {
        dfid: u32,
        name: String,
        mode: u32,
        major: u32,
        minor: u32,
        gid: u32,
    },
    Rmknod {
        qid: Qid,
    },
    Trename {
        fid: u32,
        dfid: u32,
        name: String,
    },
    Rrename,
    Treadlink {
        fid: u32,
    },
    Rreadlink {
        target: String,
    },
    Tgetattr {
        fid: u32,
        req_mask: GetattrMask,
    },
    
    Rgetattr {
        valid: GetattrMask,
        qid: Qid,
        stat: Stat,
    },
    Tsetattr {
        fid: u32,
        valid: SetattrMask,
        stat: SetAttr,
    },
    Rsetattr,
    Txattrwalk {
        fid: u32,
        newfid: u32,
        name: String,
    },
    Rxattrwalk {
        size: u64,
    },
    Txattrcreate {
        fid: u32,
        name: String,
        attr_size: u64,
        flags: u32,
    },
    Rxattrcreate,
    Treaddir {
        fid: u32,
        offset: u64,
        count: u32,
    },
    Rreaddir {
        data: DirEntryData,
    },
    Tfsync {
        fid: u32,
    },
    Rfsync,
    Tlock {
        fid: u32,
        flock: Flock,
    },
    Rlock {
        status: LockStatus,
    },
    Tgetlock {
        fid: u32,
        flock: Getlock,
    },
    Rgetlock {
        flock: Getlock,
    },
    Tlink {
        dfid: u32,
        fid: u32,
        name: String,
    },
    Rlink,
    Tmkdir {
        dfid: u32,
        name: String,
        mode: u32,
        gid: u32,
    },
    Rmkdir {
        qid: Qid,
    },
    Trenameat {
        olddirfid: u32,
        oldname: String,
        newdirfid: u32,
        newname: String,
    },
    Rrenameat,
    Tunlinkat {
        dirfd: u32,
        name: String,
        flags: u32,
    },
    Runlinkat,
    
    Tauth {
        afid: u32,
        uname: String,
        aname: String,
        n_uname: u32,
    },
    Rauth {
        aqid: Qid,
    },
    Tattach {
        fid: u32,
        afid: u32,
        uname: String,
        aname: String,
        n_uname: u32,
    },
    Rattach {
        qid: Qid,
    },
    
    Tversion {
        msize: u32,
        version: String,
    },
    Rversion {
        msize: u32,
        version: String,
    },
    Tflush {
        oldtag: u16,
    },
    Rflush,
    Twalk {
        fid: u32,
        newfid: u32,
        wnames: Vec<String>,
    },
    Rwalk {
        wqids: Vec<Qid>,
    },
    Tread {
        fid: u32,
        offset: u64,
        count: u32,
    },
    Rread {
        data: Data,
    },
    Twrite {
        fid: u32,
        offset: u64,
        data: Data,
    },
    Rwrite {
        count: u32,
    },
    Tclunk {
        fid: u32,
    },
    Rclunk,
    Tremove {
        fid: u32,
    },
    Rremove,
    
    
    
    
    
    
    
    
    
    
    
    
    
    
}
impl Fcall {
    
    pub fn fids(&self) -> Vec<u32> {
        match *self {
            Fcall::Tstatfs { fid } => vec![fid],
            Fcall::Tlopen { fid, .. } => vec![fid],
            Fcall::Tlcreate { fid, .. } => vec![fid],
            Fcall::Tsymlink { fid, .. } => vec![fid],
            Fcall::Tmknod { dfid, .. } => vec![dfid],
            Fcall::Trename { fid, dfid, .. } => vec![fid, dfid],
            Fcall::Treadlink { fid } => vec![fid],
            Fcall::Tgetattr { fid, .. } => vec![fid],
            Fcall::Tsetattr { fid, .. } => vec![fid],
            Fcall::Txattrwalk { fid, .. } => vec![fid],
            Fcall::Txattrcreate { fid, .. } => vec![fid],
            Fcall::Treaddir { fid, .. } => vec![fid],
            Fcall::Tfsync { fid, .. } => vec![fid],
            Fcall::Tlock { fid, .. } => vec![fid],
            Fcall::Tgetlock { fid, .. } => vec![fid],
            Fcall::Tlink { dfid, fid, .. } => vec![dfid, fid],
            Fcall::Tmkdir { dfid, .. } => vec![dfid],
            Fcall::Trenameat {
                olddirfid,
                newdirfid,
                ..
            } => vec![olddirfid, newdirfid],
            Fcall::Tunlinkat { dirfd, .. } => vec![dirfd],
            Fcall::Tattach { afid, .. } if afid != NOFID => vec![afid],
            Fcall::Twalk { fid, .. } => vec![fid],
            Fcall::Tread { fid, .. } => vec![fid],
            Fcall::Twrite { fid, .. } => vec![fid],
            Fcall::Tclunk { fid, .. } => vec![fid],
            Fcall::Tremove { fid } => vec![fid],
            _ => Vec::new(),
        }
    }
    
    pub fn newfid(&self) -> Option<u32> {
        match *self {
            Fcall::Txattrwalk { newfid, .. } => Some(newfid),
            Fcall::Tauth { afid, .. } => Some(afid),
            Fcall::Tattach { fid, .. } => Some(fid),
            Fcall::Twalk { newfid, .. } => Some(newfid),
            _ => None,
        }
    }
    
    pub fn qids(&self) -> Vec<Qid> {
        match *self {
            Fcall::Rlopen { qid, .. } => vec![qid],
            Fcall::Rlcreate { qid, .. } => vec![qid],
            Fcall::Rsymlink { qid } => vec![qid],
            Fcall::Rmknod { qid } => vec![qid],
            Fcall::Rgetattr { qid, .. } => vec![qid],
            Fcall::Rmkdir { qid } => vec![qid],
            Fcall::Rauth { aqid } => vec![aqid],
            Fcall::Rattach { qid } => vec![qid],
            Fcall::Rwalk { ref wqids } => wqids.clone(),
            _ => Vec::new(),
        }
    }
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Msg {
    
    
    pub tag: u16,
    
    pub body: Fcall,
}