# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) 2023
#
# @@ Meta Begin
# Application punk::mix::commandset::doc 0.1.0
# Meta platform     tcl
# Meta license      <unspecified>
# @@ Meta End



# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
##  Requirements
##e.g package require frobz

package require punk::path ;# for treefilenames, relative
package require punk::repo
package require punk::docgen ;#inline doctools - generate doctools .man files at src/docgen prior to using kettle to producing .html .md etc
package require punk::mix::cli  ;#punk::mix::cli::lib used for kettle_call
#package require punk::mix::util ;#for path_relative


# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::mix::commandset::doc {
    namespace export *

    proc _default {} {
        puts "documentation subsystem"
        puts "commands: doc.build"
        puts "        build documentation from src/doc to src/embedded using the kettle build tool"
    }

    proc build {} {
        puts "build docs"
        set projectdir [punk::repo::find_project]
        if {$projectdir eq ""} {
            puts stderr "No current project dir - unable to build docs"
            return
        }
        #user may delete the comment containing "--- punk::docgen::overwrites" and then manually edit, and we won't overwrite
        #we still generate output in src/docgen so user can diff and manually update if thats what they prefer
        set oldfiles [punk::path::treefilenames -dir $projectdir/src/doc _module_*.man]
        foreach maybedoomed $oldfiles {
            set fd [open $maybedoomed r]
            set data [read $fd]
            close $fd
            if {[string match "*--- punk::docgen overwrites *" $data]} {
                file delete -force $maybedoomed
            }
        }
        set generated [lib::do_docgen modules]
        if {[dict get $generated count] > 0} {
            #review
            set doclist [dict get $generated docs]
            set source_base [dict get $generated base]
            set target_base $projectdir/src/doc
            foreach dinfo $doclist {
                lassign $dinfo module fpath
                set relpath [punk::path::relative $source_base $fpath]
                set relfolder [file dirname $relpath]
                if {$relfolder eq "."} {
                    set relfolder ""
                }
                file mkdir [file join $target_base $relfolder]
                set target [file join $target_base $relfolder _module_[file tail $fpath]]
                puts stderr "target --> $target"
                if {![file exists $target]} {
                    file copy $fpath $target
                }
            }
        }

        if {[file exists $projectdir/src/doc]} {
            set original_wd [pwd]
            cd $projectdir/src
            #----------
            set installer [punkcheck::installtrack new project.new $projectdir/src/.punkcheck]
            $installer set_source_target $projectdir/src/doc $projectdir/src/embedded
            set event [$installer start_event {-install_step kettledoc}]
            #use same virtual id "kettle_build_doc" as project.new - review best way to keep identifiers like this in sync. 
            $event targetset_init VIRTUAL kettle_build_doc ;#VIRTUAL - since there is no specific target file - and we don't know all the files that will be generated
            $event targetset_addsource $projectdir/src/doc  ;#whole doc tree is considered the source 
            #----------
            if {\
                    [llength [dict get [$event targetset_source_changes] changed]]\
                } {
                $event targetset_started
                # -- --- --- --- --- ---
                puts stdout "BUILDING DOCS at $projectdir/src/embedded from src/doc"
                if {[catch {
                    if {"::meta" eq [info commands ::meta]} {
                        puts stderr "There appears to be a leftover ::meta command which is presumed to be from doctools. Destroying object"
                        ::meta destroy
                    }
                    punk::mix::cli::lib::kettle_call lib doc
                    #Kettle doc

                } errM]} {
                    $event targetset_end FAILED -note "kettle_build_doc failed: $errM"
                } else {
                    $event targetset_end OK
                }
                # -- --- --- --- --- ---
            } else {
                puts stderr "No change detected in src/doc"
                $event targetset_end SKIPPED
            }
            $event end
            $event destroy
            $installer destroy
            cd $original_wd
        } else {
            puts stderr "No doc folder found at $projectdir/src/doc"
        }
    }
    proc status {} {
        set projectdir [punk::repo::find_project]
        if {$projectdir eq ""} {
            puts stderr "No current project dir - unable to check doc status"
            return
        }
        if {![file exists $projectdir/src/doc]} {
            set result "No documentation source found. Expected .man files in doctools format at $projectdir/src/doc"
            return $result
        }
        set original_wd [pwd]
        cd $projectdir/src
        puts stdout "Testing status of doctools source location $projectdir/src/doc ..."
        flush stdout
        #----------
        set installer [punkcheck::installtrack new project.new $projectdir/src/.punkcheck]
        $installer set_source_target $projectdir/src/doc $projectdir/src/embedded
        set event [$installer start_event {-install_step kettledoc}]
        #use same virtual id "kettle_build_doc" as project.new - review best way to keep identifiers like this in sync. 
        $event targetset_init QUERY kettle_build_doc ;#usually VIRTUAL - since there is no specific target file - and we don't know all the files that will be generated - but here we use QUERY to ensure no writes to .punkcheck
        set last_completion [$event targetset_last_complete]

        if {[llength $last_completion]} {
            #adding a source causes it to be checksummed
            $event targetset_addsource $projectdir/src/doc  ;#whole doc tree is considered the source 
            #----------
            set changeinfo [$event targetset_source_changes]
            if {\
                    [llength [dict get $changeinfo changed]]\
                } {
                    puts stdout "changed"
                    puts stdout $changeinfo
            } else {
                puts stdout "No changes detected in $projectdir/src/doc tree"
            }
        } else {
            #no previous completion-record for this target - must assume changed - no need to trigger checksumming
            puts stdout "No existing record of doc build in .punkcheck. Assume it needs to be rebuilt."
        }


        $event destroy
        $installer destroy

        cd $original_wd
    }
    proc validate {} {
        #todo - run and validate punk::docgen output
        set projectdir [punk::repo::find_project]
        if {$projectdir eq ""} {
            puts stderr "No current project dir - unable to check doc status"
            return
        }
        if {![file exists $projectdir/src/doc]} {
            set result "No documentation source found. Expected .man files in doctools format at $projectdir/src/doc"
            return $result
        }
        set original_wd [pwd]
        set docroot $projectdir/src/doc
        cd $docroot

        dtplite validate  $docroot

        #punk::mix::cli::lib::kettle_call lib validate-doc

        cd $original_wd
    }

    namespace eval collection {
        variable pkg
        set pkg punk::mix::commandset::doc

        namespace export *
        namespace path [namespace parent]

    }

    namespace eval lib {
        variable pkg
        set pkg punk::mix::commandset::doc
        proc  do_docgen {{project_subpath modules}} {
            #Extract doctools comments from source code
            set projectdir [punk::repo::find_project]
            set output_base [file join $projectdir src docgen]
            set codesource_path [file join $projectdir $project_subpath]
            if {![file isdirectory $codesource_path]} {
                puts stderr "WARNING punk::mix::commandset::doc unable to find codesource_path $codesource_path during do_docgen - skipping inline doctools generation"
                return
            }
            if {[file isdirectory $output_base]} {
                if {[catch {
                    file delete -force $output_base
                }]}  {
                    error "do_docgen failed to delete existing output base folder: $output_base"
                }
            }
            file mkdir $output_base

            set matched_paths [punk::path::treefilenames -dir $codesource_path -antiglob_paths {**/mix/templates/** **/project_layouts/** **/decktemplates/**} *.tm]
            set count 0
            set newdocs [list] 
            set docgen_header_comments ""
            append docgen_header_comments {[comment {--- punk::docgen generated from inline doctools comments ---}]} \n
            append docgen_header_comments {[comment {--- punk::docgen DO NOT EDIT DOCS HERE UNLESS YOU REMOVE THESE COMMENT LINES ---}]} \n
            append docgen_header_comments {[comment {--- punk::docgen overwrites this file ---}]} \n
            foreach fullpath $matched_paths {
                set doctools [punk::docgen::get_doctools_comments $fullpath] 
                if {$doctools ne ""} {
                    set fname [file tail $fullpath]
                    set mod_tail [file rootname $fname] 
                    set relpath [punk::path::relative $codesource_path [file dirname $fullpath]]
                    if {$relpath eq "."} {
                        set relpath ""
                    }
                    set tailsegs [file split $relpath]
                    set module_fullname [join $tailsegs ::]::$mod_tail
                    set target_docname $fname.man
                    set this_outdir [file join $output_base $relpath]

                    if {[string length $fname] > 99} {
                        #output needs to be tarballed to do checksum change tests in a reasonably straightforward and not-too-terribly slow way.
                        #hack - review. Determine exact limit - test if tcllib tar fixed or if it's a limit of the particular tar format
                        #work around tcllib tar filename length limit ( somewhere around 100?) This seems to be a limit on the length of a particular segment in the path.. not whole path length?
                        #this case only came up because docgen used to path munge to long filenames - but left because we know there is a limit and renaming fixes it - even if it's ugly - but still allows doc generation.
                        #review - if we're checking fname - should also test length of whole path and determine limits for tar
                        package require md5
                        set target_docname [md5::md5 -hex $fullpath]_overlongfilename.man
                        puts stderr "WARNING - overlong file name - renaming $fullpath"
                        puts stderr "          to                            [file dirname $fullpath]/$target_docname"
                    }

                    file mkdir $this_outdir
                    puts stdout "saving [string length $doctools] bytes of doctools output from file $relpath/$fname"
                    set outfile [file join $this_outdir $target_docname]
                    set fd [open $outfile w]
                    fconfigure $fd -translation binary
                    puts -nonewline $fd $docgen_header_comments$doctools
                    close $fd
                    incr count
                    lappend newdocs [list $module_fullname $outfile]
                }
            }
            return [list count $count docs $newdocs base $output_base]
        }

    }
}














# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
##  Ready   
package provide punk::mix::commandset::doc [namespace eval punk::mix::commandset::doc {
    variable pkg punk::mix::commandset::doc
    variable version
    set version 0.1.0 
}]
return