let opt_of_string = function
  "" -> None
| s -> Some s

let string_of_opt = function
  None -> ""
| Some s -> s

let no_blanks s =
  let len = String.length s in
  let buf = Buffer.create len in
  for i = 0 to len - 1 do
    match s.[i] with
      ' ' | '\n' | '\t' | '\r' -> ()
    | c -> Buffer.add_char buf c
  done;
  Buffer.contents buf

let keep_alpha_nums s =
  let len = String.length s in
  let b = Buffer.create len in
  for i = 0 to len - 1 do
    match s.[i] with
      'a'..'z'
    | 'A'..'Z'
    | '0'..'9' -> Buffer.add_char b s.[i]
    | _ -> ()
  done;
  Buffer.contents b

let chop_n_char n s =
  let len = String.length s in
  if len <= n +1 or n < 0 then
    s
  else
    Printf.sprintf "%s..." (String.sub s 0 (n+1))

let string_of_date =
  let raw_string_of_date ~f_mon ~f_wday ~wday ~hours ~secs d =
    let tm = Unix.localtime d in
    let mon = f_mon tm.Unix.tm_mon in
    let day = f_wday tm.Unix.tm_wday in
    Printf.sprintf
      "%s%02d-%s-%4d%s"
      (if wday then day^", " else "")
      tm.Unix.tm_mday mon (tm.Unix.tm_year + 1900)
      (
       if hours then
         Printf.sprintf  " %02d:%02d%s"
           tm.Unix.tm_hour tm.Unix.tm_min
           (if secs then Printf.sprintf ":%02d" tm.Unix.tm_sec else "")
       else
         ""
      )
  in
  let mon_of_int_fr = function
    0 -> "Jan"
  | 1 -> "Fev"
  | 2 -> "Mar"
  | 3 -> "Avr"
  | 4 -> "Mai"
  | 5 -> "Jui"
  | 6 -> "Jul"
  | 7 -> "Aou"
  | 8 -> "Sep"
  | 9 -> "Oct"
  | 10 -> "Nov"
  | _ -> "Dec"
  in
  let wday_of_int_fr = function
    0 -> "Dimanche"
  | 1 -> "Lundi"
  | 2 -> "Mardi"
  | 3 -> "Mercredi"
  | 4 -> "Jeudi"
  | 5 -> "Vendredi"
  | _ -> "Samedi"
  in
  let mon_of_int_en = function
    0 -> "Jan"
  | 1 -> "Feb"
  | 2 -> "Mar"
  | 3 -> "Apr"
  | 4 -> "May"
  | 5 -> "Jun"
  | 6 -> "Jul"
  | 7 -> "Aug"
  | 8 -> "Sep"
  | 9 -> "Oct"
  | 10 -> "Nov"
  | _ -> "Dec"
  in
  let wday_of_int_en = function
    0 -> "Sunday"
  | 1 -> "Monday"
  | 2 -> "Tuesday"
  | 3 -> "Wednesday"
  | 4 -> "Thursday"
  | 5 -> "Friday"
  | _ -> "Saturday"
  in
  fun ?(fr=false)
    ?(wday=false) ?(hours=true) ?(secs=false) d ->
    let (f_mon, f_wday) =
      if fr then
        (mon_of_int_fr, wday_of_int_fr)
      else
        (mon_of_int_en, wday_of_int_en)
    in
    raw_string_of_date ~f_mon ~f_wday
      ~wday ~hours ~secs d

let string_of_in_channel ic =
  let len = 1024 in
  let s = String.create len in
  let buf = Buffer.create len in
  let rec iter () =
    try
      let n = input ic s 0 len in
      if n = 0 then
        ()
      else
        (
         Buffer.add_substring buf s 0 n;
         iter ()
        )
    with
      End_of_file -> ()
  in
  iter ();
  Buffer.contents buf

let first_sentence ?(limit=40) s =
  let rec get_before_dot s =
    try
      let len = String.length s in
      let n = String.index s '.' in
      if n + 1 >= len then
        (* the dot is the last character *)
        (true, s)
      else
        (
         match s.[n+1] with
           ' ' | '\n' | '\r' | '\t' ->
             (trueString.sub s 0 (n+1))
         | _ ->
             let (b, s2) = get_before_dot (String.sub s (n + 1) (len - n - 1)) in
             (b, (String.sub s 0 (n+1))^s2)
        )
    with
      Not_found -> (false, s)
  in
  let len = String.length s in
  let s_limited =
    if len > limit then
      String.sub s 0 limit
    else
      s
  in
  let (found, res) = get_before_dot s_limited in
  if found then
    res
  else
    if len > limit then
      if limit > 3 then
        (String.sub s_limited 0 (limit - 3) ^ "...")
      else
        s_limited
    else
      s

let lowercase s =
  let len = String.length s in
  let b = Buffer.create len in
  for i = 0 to len - 1 do
    let c =
      match s.[i] with
      | 'à' | 'â' | 'ä' -> 'a'
      | 'é' | 'è' | 'ê' | 'ë' -> 'e'
      | 'î' | 'ï' -> 'i'
      | 'ô' | 'ö' -> 'o'
      | 'ù' | 'û' | 'ü' -> 'u'
      | 'ç' -> 'c'
      | c -> Char.lowercase c
    in
    Buffer.add_char b c
  done;
  Buffer.contents b

let capitalize_name s =
  let len = String.length s in
  let b = Buffer.create len in
  let last_was_blank = ref true in
  for i = 0 to len - 1 do
    match s.[i] with
      '-' | '_' | ' ' | '\t' | '\r' | '\n' | '\'' ->
        last_was_blank := true ;
        Buffer.add_char b s.[i]
    | c ->
        let c2 =
          (if !last_was_blank then
             if i + 1 < len then
               match s.[i+1] with
                 '\'' -> Char.lowercase
               | c2 ->
                   match c with
                     'd' | 'D' ->
                       if i + 2 < len then
                         match s.[i+2] with
                           ' ' | '\t' | '\n' | '\r' ->
                             (match c2 with
                                'e' | 'u' | 'i' -> Char.lowercase
                              | _ -> Char.uppercase
                             )
                         | _ ->
                             Char.uppercase
                       else
                         Char.uppercase
                   | _ ->
                       Char.uppercase
             else
               Char.uppercase
           else
             Char.lowercase
          )
            c
        in
        Buffer.add_char b c2;
        last_was_blank := false;
  done;
  Buffer.contents b


let split_string ?(keep_empty=false) s chars =
  let len = String.length s in
  let rec iter acc pos =
    if pos >= len then
      match acc with
        "" -> []
      | _ -> [acc]
    else
      if List.mem s.[pos] chars then
        match acc with
          "" ->
            if keep_empty then
              "" :: iter "" (pos + 1)
            else
              iter "" (pos + 1)
        | _ -> acc :: (iter "" (pos + 1))
      else
        iter (Printf.sprintf "%s%c" acc s.[pos]) (pos + 1)
  in
  iter "" 0

let count_char s c =
  let cpt = ref 0 in
  for i = 0 to String.length s - 1 do
    if s.[i] = c then incr cpt
  done;
  !cpt

let is_prefix s1 s2 =
  let len1 = String.length s1 in
  let len2 = String.length s2 in
  (len1 <= len2) &&
    (String.sub s2 0 len1) = s1

let replace_in_string ~pat ~subs ~s =
  let len_pat = String.length pat in
  let len = String.length s in
  let b = Buffer.create len in
  let rec iter pos =
    if pos >= len then
      ()
    else
      if pos + len_pat > len then
        Buffer.add_string b (String.sub s pos (len - pos))
      else
        if String.sub s pos len_pat = pat then
          (
           Buffer.add_string b subs;
           iter (pos+len_pat)
          )
        else
          (
           Buffer.add_char b s.[pos];
           iter (pos+1);
          )
  in
  iter 0;
  Buffer.contents b

let strip_string s =
  let len = String.length s in
  let rec iter_first n =
    if n >= len then
      None
    else
      match s.[n] with
        ' ' | '\t' | '\n' | '\r' -> iter_first (n+1)
      | _ -> Some n
  in
  match iter_first 0 with
    None -> ""
  | Some first ->
      let rec iter_last n =
        if n <= first then
          None
        else
          match s.[n] with
            ' ' | '\t' | '\n' | '\r' -> iter_last (n-1)
          |        _ -> Some n
      in
      match iter_last (len-1) with
        None -> String.sub s first 1
      |        Some last -> String.sub s first ((last-first)+1)