/-
Copyright (c) 2024 Joseph Tooby-Smith. All rights reserved.
Released under Apache 2.0 license.
Authors: Joseph Tooby-Smith
-/
import PhysLean.Meta.Basic
import PhysLean.Meta.TODO.Basic
import Mathlib.Lean.CoreM
import PhysLean.Meta.Informal.Post
/-!

# Turning TODOs into YAML

-/

open Lean System Meta PhysLean


/-!

## PhysLean categories.

To be moved.

-/
inductive PhysLeanCategory
  | ClassicalMechanics
  | CondensedMatter
  | Cosmology
  | Elctromagnetism
  | Mathematics
  | Meta
  | Optics
  | Particles
  | QFT
  | QuantumMechanics
  | Relativity
  | StatisticalMechanics
  | Thermodynamics
  | Other
deriving BEq, DecidableEq

def PhysLeanCategory.string :  PhysLeanCategory → String
  | ClassicalMechanics => "Classical Mechanics"
  | CondensedMatter => "Condensed Matter"
  | Cosmology => "Cosmology"
  | Elctromagnetism => "Electromagnetism"
  | Mathematics => "Mathematics"
  | Meta => "Meta"
  | Optics => "Optics"
  | Particles => "Particles"
  | QFT => "QFT"
  | QuantumMechanics => "Quantum Mechanics"
  | Relativity => "Relativity"
  | StatisticalMechanics => "Statistical Mechanics"
  | Thermodynamics => "Thermodynamics"
  | Other => "Other"

def PhysLeanCategory.emoji : PhysLeanCategory → String
  | ClassicalMechanics => "⚙️"
  | CondensedMatter => "🧊"
  | Cosmology => "🌌"
  | Elctromagnetism => "⚡"
  | Mathematics => "➗"
  | Meta => "🏛️"
  | Optics => "🔍"
  | Particles => "💥"
  | QFT => "🌊"
  | QuantumMechanics => "⚛️"
  | Relativity => "⏳"
  | StatisticalMechanics => "🎲"
  | Thermodynamics => "🔥"
  | Other => "❓"

def PhysLeanCategory.List :  List PhysLeanCategory :=
  [ PhysLeanCategory.ClassicalMechanics,
    PhysLeanCategory.CondensedMatter,
    PhysLeanCategory.Cosmology,
    PhysLeanCategory.Elctromagnetism,
    PhysLeanCategory.Mathematics,
    PhysLeanCategory.Meta,
    PhysLeanCategory.Optics,
    PhysLeanCategory.Particles,
    PhysLeanCategory.QFT,
    PhysLeanCategory.QuantumMechanics,
    PhysLeanCategory.Relativity,
    PhysLeanCategory.StatisticalMechanics,
    PhysLeanCategory.Thermodynamics,
    PhysLeanCategory.Other]

instance : ToString PhysLeanCategory where
  toString := PhysLeanCategory.string

def PhysLeanCategory.ofFileName (n : Name) : PhysLeanCategory :=
  if n.toString.startsWith "PhysLean.ClassicalMechanics"  then
    PhysLeanCategory.ClassicalMechanics
  else if n.toString.startsWith "PhysLean.CondensedMatter" then
    PhysLeanCategory.CondensedMatter
  else if n.toString.startsWith "PhysLean.Cosmology" then
    PhysLeanCategory.Cosmology
  else if n.toString.startsWith "PhysLean.Electromagnetism" then
    PhysLeanCategory.Elctromagnetism
  else if n.toString.startsWith "PhysLean.Mathematics" then
    PhysLeanCategory.Mathematics
  else if n.toString.startsWith "PhysLean.Meta" then
    PhysLeanCategory.Meta
  else if n.toString.startsWith "PhysLean.Optics" then
    PhysLeanCategory.Optics
  else if n.toString.startsWith "PhysLean.Particles" then
    PhysLeanCategory.Particles
  else if n.toString.startsWith "PhysLean.QFT" then
    PhysLeanCategory.QFT
  else if n.toString.startsWith "PhysLean.QuantumMechanics" then
    PhysLeanCategory.QuantumMechanics
  else if n.toString.startsWith "PhysLean.Relativity" then
    PhysLeanCategory.Relativity
  else if n.toString.startsWith "PhysLean.StatisticalMechanics" then
    PhysLeanCategory.StatisticalMechanics
  else if n.toString.startsWith "PhysLean.Thermodynamics" then
    PhysLeanCategory.Thermodynamics
  else
    PhysLeanCategory.Other

/-########################-/

structure FullTODOInfo where
  content : String
  fileName : Name
  name : Name
  line : Nat
  isInformalDef : Bool
  isInformalLemma : Bool
  category : PhysLeanCategory

def FullTODOInfo.ofTODO (t : todoInfo) : FullTODOInfo :=
  {content := t.content, fileName := t.fileName, line := t.line, name := t.fileName,
   isInformalDef := false, isInformalLemma := false, category := PhysLeanCategory.ofFileName t.fileName}

unsafe def getTodoInfo : MetaM (Array FullTODOInfo) := do
  let env ← getEnv
  let todoInfo := (todoExtension.getState env)
  -- pure (todoInfo.qsort (fun x y => x.fileName.toString < y.fileName.toString)).toList
  pure (todoInfo.map FullTODOInfo.ofTODO)

def informalTODO (x : ConstantInfo) : CoreM FullTODOInfo := do
  let name := x.name
  let lineNo ← Name.lineNumber name
  let docString ← Name.getDocString name
  let file ← Name.fileName name
  let isInformalDef := Informal.isInformalDef x
  let isInformalLemma := Informal.isInformalLemma x
  let category := PhysLeanCategory.ofFileName file
  return {content := docString, fileName := file, line := lineNo, name := name,
          isInformalDef := isInformalDef, isInformalLemma := isInformalLemma, category := category}

def allInformalTODO : CoreM (Array FullTODOInfo) := do
  let x ← AllInformal
  x.mapM informalTODO

def FullTODOInfo.toYAML (todo : FullTODOInfo) : MetaM String := do
  let content := todo.content
  let contentIndent := content.replace "\n" "\n      "
  return s!"
  - file: {todo.fileName}
    githubLink: {Name.toGitHubLink todo.fileName todo.line}
    line: {todo.line}
    isInformalDef: {todo.isInformalDef}
    isInformalLemma: {todo.isInformalLemma}
    category: {todo.category}
    name: {todo.name}
    content: |
      {contentIndent}"

unsafe def allTODOs : MetaM (List FullTODOInfo) := do
  let todos ← getTodoInfo
  let informalTODOs ← allInformalTODO
  let all := todos ++ informalTODOs
  return  (all.qsort (fun x y => x.fileName.toString < y.fileName.toString)).toList

unsafe def categoriesToYML : MetaM String := do
  let todos ← allTODOs
  let mut cat := "Category:\n"
  for c in PhysLeanCategory.List do
    let num := (todos.filter (fun x => x.category == c)).length
    cat := cat ++
    s!"
  - name: \"{c}\"
    num: {num}
    emoji: \"{c.emoji}\""
  return cat ++ "\n"

unsafe def todosToYAML : MetaM String := do
  let todos ← allTODOs
  let todosYAML ← todos.mapM FullTODOInfo.toYAML
  return "TODOItem:\n" ++ String.intercalate "\n" todosYAML

unsafe def fullTODOYML : MetaM String := do
  let cat ← categoriesToYML
  let todos ← todosToYAML
  return cat ++ todos
unsafe def main (args : List String) : IO UInt32 := do
  initSearchPath (← findSysroot)
  println! "Generating TODO list."
  let ymlString ← CoreM.withImportModules #[`PhysLean] (fullTODOYML).run'
  println! ymlString
  let fileOut : System.FilePath := {toString := "./docs/_data/TODO.yml"}
  if "mkFile" ∈ args then
    IO.println (s!"TODOList file made.")
    IO.FS.writeFile fileOut ymlString
  pure 0
