/-
Copyright (c) 2024 Joseph Tooby-Smith. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joseph Tooby-Smith
-/
import HepLean.Tensors.IndexNotation.TensorIndex
/-!

# Strings of indices

A string of indices e.g. `ᵘ¹²ᵤ₄₃` is the structure we usually see
following a tensor symbol in index notation.

This file defines such an index string, and from it constructs a list of indices.

-/

open Lean
open String

namespace IndexNotation

variable (X : Type) [IndexNotation X] [Fintype X] [DecidableEq X]

/-!

## The definition of an index string

-/

/-- Takes in a list of characters and splits it into a list of characters at those characters
  which are notation characters e.g. `'ᵘ'`. -/
def stringToPreIndexList (l : List Char) : List (List Char) :=
  let indexHeads := l.filter (fun c => isNotationChar X c)
  let indexTails := l.splitOnP (fun c => isNotationChar X c)
  let l' := List.zip indexHeads indexTails.tail
  l'.map fun x => x.1 :: x.2

/-- A bool which is true given a list of characters if and only if everl element
  of the corresponding `stringToPreIndexList` correspondings to an index. -/
def listCharIsIndexString (l : List Char) : Bool :=
  (stringToPreIndexList X l).all fun l => (listCharIndex X l && l ≠ [])

/-- A string of indices to be associated with a tensor. For example, `ᵘ⁰ᵤ₂₆₀ᵘ³`. -/
def IndexString : Type := {s : String // listCharIsIndexString X s.toList = true}

namespace IndexString

/-!

## Constructing a list of indices from an index string

-/

variable {X : Type} [IndexNotation X] [Fintype X] [DecidableEq X]

/-- The character list associated with a index string. -/
def toCharList (s : IndexString X) : List Char := s.val.toList

/-! TODO: Move. -/
/-- Takes a list of characters to the correpsonding index if it exists else to `none`. -/
def charListToOptionIndex (l : List Char) : Option (Index X) :=
  if h : listCharIndex X l && l ≠ [] then
    some (IndexRep.ofCharList l (by simpa using h)).toIndex
  else
    none

/-- The indices associated to an index string. -/
def toIndexList (s : IndexString X) : IndexList X :=
  {val :=
    (stringToPreIndexList X s.toCharList).filterMap fun l => charListToOptionIndex l}

/-- The formation of an index list from a string `s` statisfying `listCharIndexStringBool`. -/
def toIndexList' (s : String) (hs : listCharIsIndexString X s.toList = true) : IndexList X :=
  toIndexList ⟨s, hs⟩

end IndexString

end IndexNotation
namespace TensorStructure

/-!

## Making a tensor index from an index string

-/

namespace TensorIndex
variable {R : Type} [CommSemiring R] (𝓣 : TensorStructure R)
variable {𝓣 : TensorStructure R} [IndexNotation 𝓣.Color] [Fintype 𝓣.Color] [DecidableEq 𝓣.Color]
variable {n m : ℕ} {cn : Fin n → 𝓣.Color} {cm : Fin m → 𝓣.Color}

open IndexNotation ColorIndexList IndexString

/-- The construction of a tensor index from a tensor and a string satisfing conditions which are
  easy to check automatically. -/
noncomputable def fromIndexString (T : 𝓣.Tensor cn) (s : String)
    (hs : listCharIsIndexString 𝓣.toTensorColor.Color s.toList = true)
    (hn : n = (toIndexList' s hs).length)
    (hD : IndexList.OnlyUniqueDuals (toIndexList' s hs))
    (hC : IndexList.ColorCond (toIndexList' s hs))
    (hd : TensorColor.ColorMap.DualMap (toIndexList' s hs).colorMap
      (cn ∘ Fin.cast hn.symm)) : 𝓣.TensorIndex :=
  TensorStructure.TensorIndex.mkDualMap T ⟨(toIndexList' s hs), hD, hC⟩ hn hd

end TensorIndex

end TensorStructure
