#!/usr/bin/env tclsh
###############################################################################
# BRLTTY - A background process providing access to the console screen (when in
#          text mode) for a blind person using a refreshable braille display.
#
# Copyright (C) 1995-2024 by The BRLTTY Developers.
#
# BRLTTY comes with ABSOLUTELY NO WARRANTY.
#
# This is free software, placed under the terms of the
# GNU Lesser General Public License, as published by the Free Software
# Foundation; either version 2.1 of the License, or (at your option) any
# later version. Please see the file LICENSE-LGPL for details.
#
# Web Page: http://brltty.app/
#
# This software is maintained by Dave Mielke <dave@mielke.cc>.
###############################################################################

source [file join [file dirname [info script]] "prologue.tcl"]

proc splitNames {names oldNamesVariable sameNamesVariable newNamesVariable} {
   global baseStrings ignorableNames

   upvar 1 $oldNamesVariable oldNames
   upvar 1 $sameNamesVariable sameNames
   upvar 1 $newNamesVariable newNames

   lassign [intersect3 $names [dict keys $baseStrings]] oldNames sameNames newNames
   lassign [intersect3 $newNames $ignorableNames] newNames x x
}

proc writeNames {path names} {
   global baseStrings
   file delete $path

   if {[llength $names] > 0} {
      if {[catch [list open $path {WRONLY TRUNC CREAT}] channel] == 0} {
         foreach name $names {
            set string [dict get $baseStrings $name]
            puts $channel "$name [dict get $string text]"
         }

         close $channel; unset channel
      } else {
         semanticError $channel
      }
   }
}

proc loadStringList {} {
   global androidDirectory

   set strings [dict create]
   set path [file join $androidDirectory STRINGS]
   logMessage detail "loading string list: $path"

   set order 0
   set pattern {^\s*(\S+)\s+(.*?)\s*$}

   if {[catch [list open $path {RDONLY}] channel] == 0} {
      while {[gets $channel line] >= 0} {
         if {[regexp $pattern $line x name text]} {
            set string [dict create text $text order [incr order]]
            dict set strings $name $string
         }
      }

      close $channel; unset channel
   } else {
      semanticError $channel
   }

   return $strings
}

proc auditStringList {} {
   global baseStrings listedStrings

   logNote "auditing string list"
   splitNames [dict keys $listedStrings] oldNames sameNames newNames
   writeNames "[getProgramName].txt" $newNames

   foreach name $oldNames {
      writeProgramMessage "old listed string: $name"
   }

   foreach name $sameNames {
      set oldText [dict get $listedStrings $name text]
      set newText [dict get $baseStrings $name text]

      if {![string equal $newText $oldText]} {
         writeProgramMessage "base string changed: $name: $oldText -> $newText"
      }
   }
}

proc loadStringResources {directory} {
   logMessage detail "loading resource directory: $directory"
   set strings [dict create]

   set pattern {^\s*<\s*string}
   append pattern {\s+name\s*=\s*"([^"]*)"}
   append pattern {(?:\s+translatable\s*=\s*"true")?}
   append pattern {(?:\s+tools:ignore\s*=\s*"(MissingTranslation)")?}
   append pattern {\s*>\s*(.*?)\s*<\s*/string\s*>\s*$}

   foreach path [glob "$directory/*.xml"] {
      logMessage detail "loading string resources: $path"

      if {[catch [list open $path {RDONLY}] channel] == 0} {
         set order 0

         while {[gets $channel line] >= 0} {
            if {[regexp $pattern $line x name ignorable text]} {
               set ignorable [expr {[string length $ignorable] > 0}]
               set string [dict create text $text ignorable $ignorable order [incr order]]
               dict set strings $name $string
            }
         }

         close $channel; unset channel
      } else {
         semanticError $channel
      }
   }

   return $strings
}

proc getIgnorableNames {strings} {
   set names [list]

   foreach name [dict keys $strings] {
      if {[dict get $strings $name ignorable]} {
         lappend names $name
      }
   }

   return $names
}

proc auditLanguage {code directory} {
   logNote "auditing language: $code"
   set languageStrings [loadStringResources $directory]
   splitNames [dict keys $languageStrings] oldNames sameNames newNames
   writeNames "[getProgramName]-$code.txt" $newNames

   foreach name $oldNames {
      writeProgramMessage "old language string: $code: $name"
   }

   if {([string length $oldNames] + [string length $newNames]) == 0} {
      global listedStrings

      foreach name [dict keys $listedStrings] {
         if {[dict get $listedStrings $name order] != [dict get $languageStrings $name order]} {
            writeProgramMessage "language string out of order: $code: $name"
         }
      }
   }
}

proc auditAllLanguages {} {
   global baseDirectory
   set languageDelimiter -

   foreach directory [glob -nocomplain -path "$baseDirectory$languageDelimiter" "?*"] {
      set code [lindex [split [file tail $directory] $languageDelimiter] 1]
      auditLanguage $code $directory
   }
}

set optionDefinitions {
}

processProgramArguments optionValues $optionDefinitions

set baseDirectory [file join $applicationDirectory res values]
set baseStrings [loadStringResources $baseDirectory]
set ignorableNames [getIgnorableNames $baseStrings]

set listedStrings [loadStringList]
auditStringList

auditAllLanguages
exit 0
