#[macro_use]
extern crate nom;
use std::fmt::{self,Debug};
use std::collections::HashMap;
use nom::IResult;
pub struct TraceList {
  pub traces: HashMap<&'static str, Trace>,
}
impl TraceList {
  pub fn new() -> Self {
    let mut traces = HashMap::new();
    traces.insert("default", Trace::new());
    TraceList { traces }
  }
  pub fn reset(&mut self, tag: &'static str) {
    let t = self.traces.entry(tag).or_insert(Trace::new());
    t.reset();
  }
  pub fn print(&self, tag: &'static str) {
    self.traces.get(tag).map(|t| t.print());
  }
  pub fn activate(&mut self, tag: &'static str) {
    let t = self.traces.entry(tag).or_insert(Trace::new());
    t.active = true;
  }
  pub fn deactivate(&mut self, tag: &'static str) {
    let t = self.traces.entry(tag).or_insert(Trace::new());
    t.active = false;
  }
  pub fn open<T>(&mut self, tag: &'static str, input: T, location: &'static str)
    where Input: From<T> {
    let t = self.traces.entry(tag).or_insert(Trace::new());
    t.open(input, location);
  }
  pub fn close<I,O:Debug,E:Debug>(&mut self, tag: &'static str, input: I, location: &'static str, result: &nom::IResult<I,O,E>)
    where Input: From<I> {
    let t = self.traces.entry(tag).or_insert(Trace::new());
    t.close(input, location, result);
  }
}
pub struct Trace {
  pub events: Vec<TraceEvent>,
  pub level: usize,
  pub active: bool,
}
impl Trace {
  pub fn new() -> Self {
    Trace {
      events: Vec::new(),
      level: 0,
      active: true,
    }
  }
  pub fn reset(&mut self) {
    self.events.clear();
    self.level = 0;
  }
  pub fn print(&self) {
    for event in self.events.iter() {
      event.print();
    }
  }
  pub fn open<T>(&mut self, input: T, location: &'static str)
    where Input: From<T> {
    if self.active {
      self.events.push(TraceEvent::new(
        self.level,
        input,
        location,
        TraceEventType::Open,
      ));
      self.level += 1;
    }
  }
  pub fn close<I,O:Debug,E:Debug>(&mut self, input: I, location: &'static str, result: &nom::IResult<I,O,E>)
    where Input: From<I> {
    if self.active {
      self.level -= 1;
      let event_type = match result {
        Ok((_,o)) => TraceEventType::CloseOk(format!("{:?}", o)),
        Err(nom::Err::Error(e)) => TraceEventType::CloseError(format!("{:?}", e)),
        Err(nom::Err::Failure(e)) => TraceEventType::CloseFailure(format!("{:?}", e)),
        Err(nom::Err::Incomplete(i)) => TraceEventType::CloseIncomplete(i.clone()),
      };
      self.events.push(TraceEvent::new(
        self.level,
        input,
        location,
        event_type
      ));
    }
  }
}
#[derive(Clone,Debug)]
pub struct TraceEvent {
  pub level: usize,
  pub input: Input,
  pub location: &'static str,
  pub event: TraceEventType,
}
#[derive(Clone,Debug)]
pub enum TraceEventType {
  Open,
  CloseOk(String),
  CloseError(String),
  CloseFailure(String),
  CloseIncomplete(nom::Needed),
}
impl TraceEvent {
  pub fn new<T>(level: usize, input: T, location: &'static str, event: TraceEventType) -> Self
    where Input: From<T> {
    TraceEvent {
      level,
      input: Input::from(input),
      location,
      event,
    }
  }
  pub fn print(&self) {
    let indent = std::iter::repeat('\t').take(self.level).collect::<String>();
    match &self.event {
      TraceEventType::Open => {
        println!("{}{}\t{:?}\n", indent, self.location, self.input);
      },
      TraceEventType::CloseOk(result) => {
        println!("{}-> Ok({})", indent, result);
      },
      TraceEventType::CloseError(e) => {
        println!("{}-> Error({})", indent, e);
      },
      TraceEventType::CloseFailure(e) => {
        println!("{}-> Failure({})", indent, e);
      },
      TraceEventType::CloseIncomplete(i) => {
        println!("{}-> Incomplete({:?})", indent, i);
      },
    }
  }
}
#[derive(Clone)]
pub enum Input {
  Bytes(*const u8, usize),
  String(*const u8, usize),
}
impl From<&[u8]> for Input {
  fn from(input: &[u8]) -> Self {
    Input::Bytes(input.as_ptr(), input.len())
  }
}
impl From<&str> for Input {
  fn from(input: &str) -> Self {
    Input::String(input.as_ptr(), input.len())
  }
}
impl Debug for Input {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match self {
      Input::String(ptr, len) => {
        let s = unsafe {
          std::str::from_utf8_unchecked(std::slice::from_raw_parts(*ptr, *len))
        };
        write!(f, "\"{}\"", s)
      },
      Input::Bytes(ptr, len) => {
        let s: &[u8] = unsafe {
          std::slice::from_raw_parts(*ptr, *len)
        };
        write!(f, "{}", to_hex(s, 16))
      }
    }
  }
}
fn to_hex(input: &[u8], chunk_size: usize) -> String {
  let mut v = Vec::with_capacity(input.len() * 3);
  let mut i = 0;
  if input.len() <= chunk_size {
    to_hex_chunk(input, i, input.len(), &mut v);
  } else {
    for chunk in input.chunks(chunk_size) {
      
      to_hex_chunk(chunk,
        i, chunk_size, &mut v);
      i += chunk_size;
      v.push(b'\n');
    }
  }
  String::from_utf8_lossy(&v[..]).into_owned()
}
static CHARS: &'static [u8] = b"0123456789abcdef";
fn to_hex_chunk(chunk: &[u8], i: usize, chunk_size: usize, v: &mut Vec<u8>) {
  let s = format!("{:08x}", i);
  for &ch in s.as_bytes().iter() {
    v.push(ch);
  }
  v.push(b'\t');
  for &byte in chunk {
    v.push(CHARS[(byte >> 4) as usize]);
    v.push(CHARS[(byte & 0xf) as usize]);
    v.push(b' ');
  }
  if chunk_size > chunk.len() {
    for _ in 0..(chunk_size - chunk.len()) {
      v.push(b' ');
      v.push(b' ');
      v.push(b' ');
    }
  }
  v.push(b'\t');
  for &byte in chunk {
    if (byte >= 32 && byte <= 126) || byte >= 128 {
      v.push(byte);
    } else {
      v.push(b'.');
    }
  }
}
thread_local! {
  pub static NOM_TRACE: ::std::cell::RefCell<TraceList> = ::std::cell::RefCell::new(TraceList::new());
}
#[macro_export]
macro_rules! print_trace (
 () => {
  $crate::NOM_TRACE.with(|trace| {
    trace.borrow().print("default");
  });
 };
 ($tag:expr) => {
  $crate::NOM_TRACE.with(|trace| {
    trace.borrow().print($tag);
  });
 };
);
#[macro_export]
macro_rules! reset_trace (
 () => {
  $crate::NOM_TRACE.with(|trace| {
    trace.borrow_mut().reset("default");
  });
 };
 ($tag:expr) => {
  $crate::NOM_TRACE.with(|trace| {
    trace.borrow_mut().reset($tag);
  });
 };
);
#[macro_export]
macro_rules! activate_trace (
 () => {
  $crate::NOM_TRACE.with(|trace| {
    trace.borrow_mut().activate("default");
  });
 };
 ($tag:expr) => {
  $crate::NOM_TRACE.with(|trace| {
    trace.borrow_mut().activate($tag);
  });
 };
);
#[macro_export]
macro_rules! deactivate_trace (
 () => {
  $crate::NOM_TRACE.with(|trace| {
    trace.borrow_mut().deactivate("default");
  });
 };
 ($tag:expr) => {
  $crate::NOM_TRACE.with(|trace| {
    trace.borrow_mut().deactivate($tag);
  });
 };
);
fn tr<I,O,E,F>(tag: &'static str, name: &'static str, f: F) -> impl Fn(I) -> IResult<I,O,E>
  where Input: From<I>,
        F: Fn(I) -> IResult<I,O,E>,
        I: Clone,
        O: Debug,
        E: Debug {
  move |i: I| {
    let input1 = i.clone();
    let input2 = i.clone();
    NOM_TRACE.with(|trace| {
      (*trace.borrow_mut()).open(tag, input1, name);
    });
    let res = f(i);
    NOM_TRACE.with(|trace| {
      (*trace.borrow_mut()).close(tag, input2, name, &res);
    });
    res
  }
}
#[macro_export]
macro_rules! tr (
  ($i:expr, $name:ident, $submac:ident!( $($args:tt)* )) => (
    tr!(__impl $i, "default", stringify!($name), $submac!($($args)*))
  );
  ($i:expr, $name:ident, $f:expr) => (
    tr!(__impl $i, "default", stringify!($name), call!($f))
  );
  ($i:expr, $submac:ident!( $($args:tt)* )) => (
    tr!(__impl $i, "default", stringify!($submac), $submac!($($args)*))
  );
  ($i:expr, $f:expr) => (
    tr!(__impl $i, "default", stringify!($f), call!($f))
  );
  (__impl $i:expr, $name:expr, $submac:ident!( $($args:tt)* )) => (
    tr!(__impl $i, "default", $name, $submac!($($args)*))
  );
  (__impl $i:expr, $submac:ident!( $($args:tt)* )) => (
    tr!(__impl $i, "default", $submac!($($args)*))
  );
  (__impl $i:expr, $f:expr) => (
    tr!(__impl $i, "default", $f)
  );
  ($i:expr, $tag:expr, $name:ident, $submac:ident!( $($args:tt)* )) => (
    tr!(__impl $i, $tag, stringify!($name), $submac!($($args)*))
  );
  ($i:expr, $tag:expr, $name:ident, $f:expr) => (
    tr!(__impl $i, $tag, stringify!($name), call!($f))
  );
  ($i:expr, $tag:expr, $submac:ident!( $($args:tt)* )) => (
    tr!(__impl $i, $tag, stringify!($submac), $submac!($($args)*))
  );
  ($i:expr, $tag:expr, $f:expr) => (
    tr!(__impl $i, $tag, stringify!($f), call!($f))
  );
  (__impl $i:expr, $tag:expr, $name:expr, $submac:ident!( $($args:tt)* )) => (
    {
      let input = $i;
      $crate::NOM_TRACE.with(|trace| {
        (*trace.borrow_mut()).open($tag, input, $name);
      });
      let res = $submac!(input, $($args)*);
      $crate::NOM_TRACE.with(|trace| {
        (*trace.borrow_mut()).close($tag, input, $name, &res);
      });
      res
    }
  );
  (__impl $i:expr, $tag:expr, $submac:ident!( $($args:tt)* )) => (
    {
      let input = $i;
      $crate::NOM_TRACE.with(|trace| {
        (*trace.borrow_mut()).open($tag, input, stringify!($submac));
      });
      let res = $submac!(input, $($args)*);
      $crate::NOM_TRACE.with(|trace| {
        (*trace.borrow_mut()).close($tag, input, $name, &res);
      });
      res
    }
  );
  (__impl $i:expr, $tag:expr, $f:expr) => (
    {
      let input = $i;
      $crate::NOM_TRACE.with(|trace| {
        (*trace.borrow_mut()).open($tag, input, stringify!($f));
      });
      let res = $f(input);
      $crate::NOM_TRACE.with(|trace| {
        (*trace.borrow_mut()).close($tag, input, $name, &res);
      });
      res
    }
  );
);
#[cfg(test)]
mod tests {
  use super::*;
  use nom::character::complete::digit1 as digit;
  #[test]
  pub fn trace_bytes_parser() {
    named!(parser<Vec<&[u8]>>,
      tr!(preceded!(
        tr!(tag!("data: ")),
        tr!(delimited!(
          tag!("("),
          separated_list!(
            tr!(tag!(",")),
            tr!(digit)
          ),
          tr!(tag!(")"))
        ))
      ))
    );
    println!("parsed: {:?}", parser(&b"data: (1,2,3)"[..]));
    print_trace!();
    reset_trace!();
    panic!();
  }
  #[test]
  pub fn trace_str_parser() {
    named!(parser<&str, Vec<&str>>,
      tr!(ROOT, preceded!(
        tr!(tag!("data: ")),
        tr!(PARENS, delimited!(
          tag!("("),
          separated_list!(
            tr!(tag!(",")),
            tr!(digit)
          ),
          tr!(tag!(")"))
        ))
      ))
    );
    deactivate_trace!();
    activate_trace!();
    println!("parsed: {:?}", parser("data: (1,2,3)"));
    print_trace!();
    reset_trace!();
    panic!();
  }
}