namespace eval shellspy::callbacks {
    package require shellfilter
    
    
  #each member of args - is not itself a list - and cannot be treated as one.
  #things like [concat {*}args] will generall break things further down the line
    proc cmdshellb {args} {
        shellfilter::log::open callback_cmdb {-syslog 127.0.0.1:514}
        shellfilter::log::write callback_cmdb "cmdshellb got [llength $args] arguments"
        shellfilter::log::write callback_cmdb "cmdshellb got  '$args'"
        
        if {[lindex $args 0] eq "cmdb"} {
            set curlyparts [lrange $args 1 end]
            shellfilter::log::write callback_cmdb "cmdshellb curlyparts  '$curlyparts'"
            #we lose grouping info by joining like this..
            #set tail [string trim [join $curlyparts " "]]
            set tailinfo [shellfilter::list_element_info $curlyparts]
            
            shellfilter::log::write callback_cmdb "cmdshellb tailinfo  '$tailinfo'"
            #e.g
            #% set y {{"c:/test/etc"} true {'blah1'} {'blah 2'}}
            #% lappend y  \\\\\\
            #{"c:/test/etc"} true {'blah1'} {'blah 2'} \\\\\\
            #% foreach i [shellfilter::list_element_info $y] {puts $i}
            # 0
            # wouldbrace 1 wouldescape 0 has_braces 0 has_inner_braces 0 apparentwrap doublequotes head_tail_chars {{"} {"}} head_tail_names {dq dq} len 13 difflen 2
            # 1
            # wouldbrace 0 wouldescape 0 has_braces 0 has_inner_braces 0 apparentwrap not-determined head_tail_chars {t e} head_tail_names {t e} len 4 difflen 0
            # 2
            # wouldbrace 0 wouldescape 0 has_braces 0 has_inner_braces 0 apparentwrap singlequotes head_tail_chars {' '} head_tail_names {sqote sqote} len 7 difflen 0
            # 3
            # wouldbrace 1 wouldescape 0 has_braces 0 has_inner_braces 0 apparentwrap singlequotes head_tail_chars {' '} head_tail_names {sqote sqote} len 8 difflen 2
            # 4
            # wouldbrace 0 wouldescape 1 has_braces 0 has_inner_braces 0 apparentwrap not-determined head_tail_chars {\\ \\} head_tail_names {\\ \\} len 3 difflen 3
            
            
            #sample arglist - 7 items
            #((c:\lua\luajit.exe
            #C:\Users\sleek\vimfiles\plugged\vim-flog/lua/flog/graph_bin.lua
            #__START
            #true
            #git -C C:/repo/3p/ansi-to-html --literal-pathspecs log --parents --topo-order --no-color --pretty=format:__START\%n\%h\%n\%p\%n\%D\%n\%ad\ \[\%h\]\ \{\%an\}\%d\ \%s --date=iso --no-merges --max-count=2000 -- )
            #>
            #C:\Users\sleek\AppData\Local\Temp\VRR6A67.tmp)
            
            
            #complex method.. lists at levels..
            set taillist [list]
            set level 0 ;#bracket depth.. incr each opening bracket "("
            set leveldict [dict create]
            dict set leveldict 0 [list]
            
            #simpler method.. string
            set output ""
            dict for {idx info} $tailinfo {
                set item [lindex $curlyparts $idx]
                set itemlen [string length $item]
                if {[dict get $info apparentwrap] eq "brackets"} {
                    set chars [split $item ""]
                    set opening [lsearch -all $chars "("]
                    set closing [lsearch -all $chars ")"]
                    if {[llength $opening] == [llength $closing]} {
                        #dict lappend leveldict 0 $item ;#element individually wrapped in brackets and balanced, pass through
                        append output "$item "
                    } else {
                        #robustness warning: we examine outer brackets only, and are ignoring that things like {((a)(b)} {((a)(b}
                        #  are composed of multiple elements for now ..  as there should have been a space between the (a) & (b) elements anyway,
                        #  in which case we would only see things like {((a)} or {(a}
                        set ltrimmed [string trimleft $item "("]
                        set countleft [expr {$itemlen - [string length $ltrimmed]}]
                        
                        set rtrimmed [string trimright $item ")"]
                        set countright [expr {$itemlen - [string length $rtrimmed]}]
                        
                        #simpler method..
                        append output "$item "
                        
                    }
                } else {
                    set lcharname [lindex [dict get $info head_tail_names] 0]
                    set rcharname [lindex [dict get $info head_tail_names] 1]
                    
                    if {$lcharname eq "lbracket"} {
                        set ltrimmed [string trimleft $item "("]
                        set countleft [expr {$itemlen - [string length $ltrimmed]}]
                        set braces [string repeat "(" $countleft]
                        
                        set testlist [list]
                        lappend testlist $ltrimmed
                        set testinfo [shellfilter::list_element_info $testlist]
                        set testelement [dict get $testinfo 0]
                        if {[dict get $testelement wouldbrace]} {
                            #append output "${braces}\"$ltrimmed\" "
                            append output "${braces} $ltrimmed "
                        } else {
                            append output "${braces} $ltrimmed "
                        }
                    
                    } elseif {$rcharname eq "rbracket"} {
                        set rtrimmed [string trimright $item ")" ]
                        set countright [expr {$itemlen - [string length $rtrimmed]}]
                        set braces [string repeat ")" $countright]
                        set testlist [list]
                        lappend testlist $rtrimmed
                        set testinfo [shellfilter::list_element_info $testlist]
                        set testelement [dict get $testinfo 0]
                        if {[dict get $testelement wouldbrace]} {
                            #append output "\"$rtrimmed\"${braces} "
                            if {[string first " " $rtrimmed] > 0} {
                                append output "\"$rtrimmed\" ${braces} "
                            } else {
                                append output "$rtrimmed ${braces} "
                            }
                        } else {
                            append output "${rtrimmed} ${braces} "
                        }
                        
                    
                    } else {
                        set testlist [list]
                        lappend testlist $item
                        set testinfo [shellfilter::list_element_info $testlist]
                        set testelement [dict get $testinfo 0]
                        if {[dict get $testelement wouldbrace]} {
                            #append output "\"$item\" "
                            if {[string first " " $item] > 0} {
                                append output "\"$item\" "
                            } else {
                                append output "$item "
                            }
                        } else {
                            append output "$item "
                        }
                    }
                    
                }
            
            }
            shellfilter::log::write callback_cmdb "cmdshellb about to parse_cmd_brackets '$output'"
            #$output now has quoted all items that 'wouldbrace' 
            set curly_list [shellfilter::parse_cmd_brackets $output]
            if {[llength $curly_list] == 1} {
                #we expect the whole set of arguments was wrapped in a set of brackets
                set curly_items [lindex $curly_list 0]
            } else {
                #unexpected.. root level of string had multiple bracketed sections
                #try using the curly_list directly warn?
                set curly_items $curly_list
            }
            #e.g
            # ((c:\lua\luajit.exe -v) > C:\Users\sleek\AppData\Local\Temp\V7NCBF.tmp)
            #=>
            # {{{c:\lua\luajit.exe} -v} > {C:\Users\sleek\AppData\Local\Temp\V7NCBF.tmp}}
            
            #what is the proper way to flatten?
            
            set comspec [lindex $curly_items 0]
            if {[llength $comspec] >1} {
                set commandlist [concat $comspec [lrange $curly_items 1 end]]    
            } else {
                set commandlist $curly_items
            }
            
            set commandlist [floghack_singlearg callback_cmdb {*}$commandlist]
            return $commandlist
        } else {
            shellfilter::log::write callback_cmdb "first arg: [lindex $args 0] vs 'cmdb'"
            error "first arg: [lindex $args 0] vs 'cmdb'"
            #return $args
        }
    }
    proc cmdshell {args} {
        if {[catch {
            set args [floghack_singlearg callback_cmdshell {*}$args]
        } errMsg]} {
            error "FLOGHACK callback_cmdshell error $errMsg"
        }


      #set args [concat [lindex $args 0] [lrange $args 1 end]]
      return $args
    }
    proc cmdshelluc {args} {
        if {[catch {
            set args [floghack_singlearg callback_cmdshelluc {*}$args]
        } errMsg]} {
            error "FLOGHACK callback_cmdshelluc error $errMsg"
        }

        #set args [concat [lindex $args 0] [lrange $args 1 end]]
        return $args
    }
  
  proc powershell {args} {
        if {[catch {
            set args [floghack "callback_powershell" {*}$args]
        } errMsg]} {
            error "FLOGHACK callback_powershell error $errMsg"
        }
        return $args
      
  }
  proc raw {args} {
        if {[catch {
            set args [floghack_singlearg "callback_raw" {*}$args]
        } errMsg]} {
            error "FLOGHACK callback_raw error $errMsg"
        }
      #set args [concat [split [lindex $args 0]] [lrange $args 1 end]] ;#definitely bad!
      

      return $args
  }

  #hack for c: drive - extend as necessary
  #todo - customize! 
  # various hacks may be necessary for home dirs temp files etc!
  proc sh {args} {
        if {[catch {
            set args [floghack callback_sh {*}$args]
        } errMsg]} {
            error "FLOGHACK callback_sh error $errMsg"
        }



    set final [list]
    foreach a $args {
      if {[string match -nocase {*c:/*} $a]} {
        set a [string map [list {c:/} {/c/}] $a]
        lappend final $a
      } elseif {[string match -nocase {*c:\\*} $a]} {
        set a [string map [list \\ / ] $a]
        set a [string map [list {c:/} {/c/} {C:/} {/C/}] $a]
        lappend final $a 
      } else {
        lappend final $a
      }
    }
    return $final
  }
  proc wsl {args} {
    set args [floghack_singlearg callback_wsl {*}$args]
    #wsl bash-style /mnt/c paths to be convertd
    set args [convert_mnt_win {*}$args]
    
    #review - seems bad
    #flatten first element which arrives wrapped
    #set args [concat [lindex $args 0] [lrange $args 1 end]]
    
    
    return $args
  }
  proc bash {args} {
    set args [floghack callback_bash {*}$args]
    return [convert_mnt_win {*}$args]
  }



  #helper functions
  proc convert_mnt_win {args} {
    set final [list]
    foreach a $args {
      if {[string match -nocase {*c:\\*} $a]} {
         set a [string map [list \\ / ] $a]
      }
      #bash seems to be strict about lowercase /mnt/c
      if {[string match -nocase {*c:/*} $a]} {
        set a [string map [list {c:/} {/mnt/c/} {C:/} {/mnt/c/}] $a]
      }
      lappend final $a
    }
    shellfilter::log::write callback_convert_mnt_win "convert_mnt_win commandlist '$final'"
    return $final
  }

  #when we get the git command and args together as one element in the commandlist
  proc floghack_singlearg {logtag args} {
     #return $args
     shellfilter::log::write $logtag "floghack_singlearg got $logtag '$args'"
     set newargs [list]
     foreach a $args {
        if {[string match "*pretty=format:__START*" $a]} {
          set a [string map [list "pretty=format:__" "pretty=format:\"__"] $a]
          set a [string map [list " --date=" "\" --date="] $a]
          set a [string map [list \\ ""] $a] ;# a bad idea?
          lappend newargs $a
        } else {
          lappend newargs $a
        }
     }
     shellfilter::log::write $logtag "floghack_singlearg hacked commandlist '$newargs'"
     return $newargs  
  }


  proc floghack {logtag args} {
  
    #return $args
    shellfilter::log::write $logtag "floghack got [llength $args] args: '$args'"
    set indent "    "
    set i 0
    foreach a $args {
      shellfilter::log::write $logtag "floghack ${indent}$i $a"
      incr i
    }

    #Flog/luajit hack
    #wrong way
    #if {[string first "pretty=format:__START" $args] > 0} {
    #  set args [string map [list "pretty=format:" "pretty=format:'" " --date=" "' --date=" ] $args]
    #}
    set newargs [list]
    
    set pretty ""
    set pstart [lsearch $args "--pretty=format:__START*"]
    set pafter [lsearch $args "--date=*"]
    if {$pstart > 0} {
      set newargs [lrange $args 0 $pstart-1]
      set parts [lrange $args $pstart $pafter-1]
      set i 1
      foreach p $parts {
        if {$i == 1} {
          set pretty [string map [list "format:__" "format:\"__"] $p]
          #set pretty $p
        } else {
          append pretty " $p"
        }
        if {$i == [llength $parts]} {
          append pretty "\""
        }
        incr i
      }
      set pretty [string map [list \\ ""] $pretty]
      lappend newargs $pretty
      #lappend newargs "--pretty=format:%h"
      set newargs [concat $newargs [lrange $args $pafter end]] ;#concat of 2 lists.. should be ok
    } else {
      set newargs $args
    }


    shellfilter::log::write $logtag "floghack hacked commandlist '$newargs'"


    return $newargs
  }







}