# Conventions
[ ] Means not done
[R] Means done by an ai agent, but needs a human review 
[X] Means done


# List of tasks
- [X] Unify error reporting. Create class ErrorHandler that get notified of every catched exception. This class prints the exception in stdout.
- [X] Make formula description collapsable in FormulaScreen. Initialy, the description is visible, but the user can hide it.
- Refactor formula and unit loading:
  - [X] Create method `List<Object?> parseD4rtLiteral(String arrayStringLiteral). It parses a d4rt array literal (containing maps and arrays) to a List<Object?> using d4rt
  - [X] Make `fromArrayStringLiteral` from UnitSpec and Formula to use parseD4rtLiteral
  - [X] Create function `List<Object> parseCorpusElements(String arrayStringLiteral)`. It uses parseD4rtLiteral and determines if each element of the array is a formula or a unit. Then converts the objects with Formula.fromSet or UnitSpec.fromSet.
  - [X] Create method loadFormulaElements( List<Object> elements). Tipically receives the list from parseCorpusElements(). It loads the units first, then the formulas, to avoid missing dependencies.
  - [X] Change createDefaultCorpus to use loadFormulaElements instead of loadUnits and loadFormula. Make loadUnits and loadFormula private.
- [X] Use a single table in database `FORMULAELEMENT` to store formulas and units. The table contains only two columns: autonumeric id and text.
- Drift files have a lot of duplicate code. "web" version is the same as native version, only _openConnection is diferrent. Refactor to not duplicate code.
- [X] Create Formula.toStringLiteral. It is the reverse of Formula.fromSet( Formula.fromArrayStringLiteral(string)[0] )
- [X] Create UnitSpec.toStringLiteral, like Formula.toStringLiteral
- [X] Make Formula and UnitSpec subclasses of FormulaElement. Change return type of functions that return Object to FormulaElement if necessary.
- [X] Define toStringLiteral in FormulaElement.
- [X] Database file location:
  - [X] In linux, the sqlite database file will be located following rules at https://specifications.freedesktop.org/basedir/latest/
  - [X] In Windows, the sqlite database file will be in %appdata%/Roaming
  - [X] In Macos, the sqlite database file will be in ~/Library/Application Support
- [X] Initialize database at startup
  - [X] Try first to load a corpus from database
  - [X] If the database is empty, sugest to use a default corpus
  - [X] If the user choose to use the default corpus, populate de database with the default corpus (load defaultcorpus, and then use toStringLiteral). If not, start with an empty list of formulas.
  - [X] From now on, the corpus will be loaded from database instead of assets
- [X] Create method List<FormulaElement> Corpus.withDependencies(Formula formula). It will return the formula, the units of the formula, and all the units from the corpus with the same base unit.
- [X] Add a Share button to the formula list. It will export the array string literal of the formula with the units from Corpus.withDependencies().
- [X] Replace flutter-markdown with flutter-markdown-plus
- [X] Heron's formula: investigate why a=3, b=40, c=5 yields NaN. Root cause: input values don't form a valid triangle (violate triangle inequality: 3+5=8 is not > 40). Added documentation note to the formula description.
- [X] Refactor ./assets/formulas d4rt files:
  - [X] Pretty print files as dart literals (like JSON, but allow raw strings r"""like this""")
  - [X] Ensure there is no formula duplicates. If necesary, move or delete the formula in file formulas.d4rt
  - [X] defaultCorpus must load all formula files
- [X] Create a formula in ./assets/formulas/networking.d4art: input is a string with ip address and mask, output is ip subnet of this address and broadcast address.
- [R] Develop a new screen that edits a formula in ./lib/ai directory:
  - [R] FormulaEditor initializes with a Formula
  - [R] A textfield allows editing the "name" of the formula
  - [R] A text area allows editing the "description". A button pops up a preview of the markdown.
  - [R] There is one row per input variable. The "name" is a textfield. A first drop down allows to select the base unit, and a second dropdown is populated with all derived units of the selected base unit, and the base unit. The unit of the input variable is the derived unit.
  - [R] Each input variable can be deleted with a button
  - [R] A button after the inputs variables allows to insert a new input variable
  - [R] There is one row for the ouput variable, similar to the row for the input variable
  - [R] d4rtCode is a text area with dart syntax highligthing
  - [R] At the botton, a button allows to test the edited Formula, launching a FormulaScreen
- [X] Create SetUtils.prettyPrint(): receives a dynamic Set, Array, string or number. Convert to a dart representation of than value (a set/array literal), json-like, but for dart language. Do it recursivelly on local functions to that method:
  - _prettyPrintString(String s, int indent): Only for simple strings
  - _prettyPrintNumber(Number n, int indent)
  - _prettyPrintSet(Set s, int indent)
  - _prettyPrintArray(dynamic[] a, int indent)
  - _prettyPrintRawString(String s, int indent): Use _prettyPrintRawString when the string contains newlines, $, backlash...
- [X] Add a field to Formula: UUID.
  - A constructor without UUID will generate a new random UUID. A constructor with UUID will use the provided UUID.
  - The field should be used in database and everywhere instead of the name. The name is not unique anymore, but the UUID is.
  - This will be used to identify formulas, instead of the name. This way, we can have formulas with the same name but different UUIDs. The name is not unique anymore. Corpus will be a list of UUIDs, instead of a list of formulas. The corpus.getFormula() method will return the first formula with that name.
- [R] When _FormulaScreenState._evaluateFormula() detect an error, instead of show an SnackBar, show a ExpansionTile with "⚠️ There were an error. Show details..." with the details of the exception. The ExpansionTile will be invisible if there is no error.
- [R] When FormulaEditor._save formula, ensure formula is updated in the initial FormulaList
- [R] In FormulaEditor, add a button to "Save as copy", additional to the existing button "Save". It doesnt matter if the copy has the same name as the original formula, since they are identified by a internal UUID.
- [R] Add a button for each input variable in FormulaScreen.
  - This button will create a DerivedFormula, with the input variable as output, and the rest of the input variables as inputs.
  - The DerivedFormula will then be displayed in the FormulaScreen
- [R] If the Formula displayed in FormulaScreen is a DerivedFormula, the edit button will be disabled
- [R] When a formula is derived in FormulaScreen, the new FormulaScreen is not pushed in navigator, it replacles the current FormulaScreen
- [R] In FormulaScreen, a Formula cant be derived if DerivedFormula.isDerivable() returns false
- [R] The algorithm of formulaSolver should be https://en.wikipedia.org/wiki/Newton%27s_method
- [R] Use receive_sharing_intent package to implement import of files in linux and android.
  - The application will accept *.d4rtf files with the same format of files in ./assets .
  - The application will accept also shared text, with same format as files in ./assets.
  - The loaded formulaelemets will be added to the GetIt.instance.get<Corpus>()
- [R] Preview of imported formulalements
  - The screen will receive a list of FormulaElements to import
  - The formulas will have a "edit" button to show a FormulaEditor with the formula
  - The screen will have an "import all" button to import all the FormulaElements in the list. This will call Corpus.addFormulaElement() for each element, and then pop the screen.
- [R] In FormulaList, add a button next to "export" to import FormulaElements.
  - It will show a screen with a text editor with dart syntax and a button "paste".
  - The "paste" button will copy the clipboard into the text editor.
  - A second button "import" will use the import preview screen
 -[X] Launch test app_test.dart. Iterate until the test pass.
- [R] Add test to app_test.dart: share first formula to clipboard and import it
- [R] Unify UUID and id of FormulaElement
  - UUID is in memory
  - id is in database
  - remove id from database, add UUID to database
- [X] Solve exception in _CorpusLoaderState.build() when GetIt.instance.registerSingleton<Corpus>(corpus) after importing formula, since there is already registeted.
- [R] When importing FormulaElements, save the FormulaElements in the database (currently, they are only added to the Corpus in memory).
- [ ] Include an http server in the application (linux and android).
  - Add a rule in Makefile to create a zip file  with the contents of ./build/web in the ./assets/generated directory -> ./assets/generated/webapp.zip
  - Add webapp.zip as a flutter asset
  - In the /static path, serve the files contained in webapp.zip
- [X] Ensure database is loaded if the file exist, and not use default corpus allways.
- [ ] Ensure more room for formula title in FormulaScreen. Maybe a marquee or another row for buttons or both.
- [ ] In android, images in description are not shown.
- [ ] Make formulaSolver() asyncronous, and show a CircularProgressIndicator inside the output variable while the formula is being solved. Honor a new optinal parameter "timeout" in formulaSolver, that will throw a TimeoutException.
