/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
module

prelude
public import Init.Data.String.Basic
import all Init.Data.String.Defs
import Init.Data.String.Lemmas.Order
import Init.Data.String.Lemmas.Basic
import Init.Data.String.OrderInstances
import Init.Grind

public section

namespace String

namespace Slice

theorem isEmpty_eq {s : Slice} : s.isEmpty = (s.utf8ByteSize == 0) :=
  (rfl)

theorem isEmpty_iff {s : Slice} :
    s.isEmpty ↔ s.utf8ByteSize = 0 := by
  simp [Slice.isEmpty_eq]

theorem startPos_eq_endPos_iff {s : Slice} :
    s.startPos = s.endPos ↔ s.isEmpty := by
  rw [eq_comm]
  simp [Slice.Pos.ext_iff, Pos.Raw.ext_iff, Slice.isEmpty_iff]

theorem startPos_ne_endPos_iff {s : Slice} :
    s.startPos ≠ s.endPos ↔ s.isEmpty = false := by
  simp [Slice.startPos_eq_endPos_iff]

theorem startPos_ne_endPos {s : Slice} : s.isEmpty = false → s.startPos ≠ s.endPos :=
  Slice.startPos_ne_endPos_iff.2

theorem isEmpty_iff_forall_eq {s : Slice} :
    s.isEmpty ↔ ∀ (p q : s.Pos), p = q := by
  rw [← Slice.startPos_eq_endPos_iff]
  refine ⟨fun h p q => ?_, fun h => h _ _⟩
  apply Std.le_antisymm
  · apply Std.le_trans (Pos.le_endPos _) (h ▸ Pos.startPos_le _)
  · apply Std.le_trans (Pos.le_endPos _) (h ▸ Pos.startPos_le _)

theorem isEmpty_eq_false_of_lt {s : Slice} {p q : s.Pos} :
    p < q → s.isEmpty = false := by
  rw [← Decidable.not_imp_not]
  simp
  rw [Slice.isEmpty_iff_forall_eq]
  intro h
  cases h p q
  apply Std.lt_irrefl

@[simp]
theorem isEmpty_sliceFrom {s : Slice} {p : s.Pos} :
    (s.sliceFrom p).isEmpty ↔ p = s.endPos := by
  simp [← startPos_eq_endPos_iff, ← Pos.ofSliceFrom_inj]

@[simp]
theorem isEmpty_sliceFrom_eq_false_iff {s : Slice} {p : s.Pos} :
    (s.sliceFrom p).isEmpty = false ↔ p ≠ s.endPos :=
  Decidable.not_iff_not.1 (by simp)

@[simp]
theorem isEmpty_sliceTo {s : Slice} {p : s.Pos} :
    (s.sliceTo p).isEmpty ↔ p = s.startPos := by
  simp [← startPos_eq_endPos_iff, eq_comm (a := p), ← Pos.ofSliceTo_inj]

@[simp]
theorem isEmpty_sliceTo_eq_false_iff {s : Slice} {p : s.Pos} :
    (s.sliceTo p).isEmpty = false ↔ p ≠ s.startPos :=
  Decidable.not_iff_not.1 (by simp)

end Slice

theorem isEmpty_eq_utf8ByteSize_beq_zero {s : String} : s.isEmpty = (s.utf8ByteSize == 0) :=
  (rfl)

theorem isEmpty_iff_utf8ByteSize_eq_zero {s : String} : s.isEmpty ↔ s.utf8ByteSize = 0 := by
  simp [isEmpty_eq_utf8ByteSize_beq_zero]

@[simp]
theorem isEmpty_iff {s : String} : s.isEmpty ↔ s = "" := by
  simp [isEmpty_iff_utf8ByteSize_eq_zero]

@[simp]
theorem isEmpty_eq_false_iff {s : String} : s.isEmpty = false ↔ s ≠ "" := by
  simp [← isEmpty_iff]

theorem startPos_ne_endPos_iff {s : String} : s.startPos ≠ s.endPos ↔ s ≠ "" := by
  simp

theorem startPos_ne_endPos {s : String} : s ≠ "" → s.startPos ≠ s.endPos :=
  startPos_ne_endPos_iff.2

@[simp]
theorem isEmpty_toSlice {s : String} : s.toSlice.isEmpty = s.isEmpty := by
  simp [isEmpty_eq_utf8ByteSize_beq_zero, Slice.isEmpty_eq]

theorem isEmpty_toSlice_iff {s : String} : s.toSlice.isEmpty ↔ s = "" := by
  simp

theorem Slice.isEmpty_copy {s : Slice} : s.copy.isEmpty = s.isEmpty := by
  rw [isEmpty_eq_utf8ByteSize_beq_zero, Slice.utf8ByteSize_copy, isEmpty_eq]

@[simp]
theorem Slice.copy_eq_empty_iff {s : Slice} : s.copy = "" ↔ s.isEmpty := by
  simp [← Slice.isEmpty_copy]

theorem Slice.copy_ne_empty_iff {s : Slice} : s.copy ≠ "" ↔ s.isEmpty = false := by
  simp

theorem eq_empty_iff_forall_eq {s : String} : s = "" ↔ ∀ (p q : s.Pos), p = q := by
  rw [← isEmpty_toSlice_iff, Slice.isEmpty_iff_forall_eq]
  exact ⟨fun h p q => by simpa [Pos.toSlice_inj] using h p.toSlice q.toSlice,
    fun h p q => by simpa [Pos.ofToSlice_inj] using h (Pos.ofToSlice p) (Pos.ofToSlice q)⟩

theorem ne_empty_of_lt {s : String} {p q : s.Pos} :
    p < q → s ≠ "" := by
  rw [← Pos.toSlice_lt_toSlice_iff, ne_eq, ← isEmpty_toSlice_iff, Bool.not_eq_true]
  exact Slice.isEmpty_eq_false_of_lt

@[simp]
theorem isEmpty_sliceFrom {s : String} {p : s.Pos} :
    (s.sliceFrom p).isEmpty ↔ p = s.endPos := by
  simp [← Slice.startPos_eq_endPos_iff, ← Pos.ofSliceFrom_inj]

@[simp]
theorem isEmpty_sliceFrom_eq_false_iff {s : String} {p : s.Pos} :
    (s.sliceFrom p).isEmpty = false ↔ p ≠ s.endPos :=
  Decidable.not_iff_not.1 (by simp)

@[simp]
theorem isEmpty_sliceTo {s : String} {p : s.Pos} :
    (s.sliceTo p).isEmpty ↔ p = s.startPos := by
  simp [← Slice.startPos_eq_endPos_iff, eq_comm (a := p), ← Pos.ofSliceTo_inj]

@[simp]
theorem isEmpty_sliceTo_eq_false_iff {s : String} {p : s.Pos} :
    (s.sliceTo p).isEmpty = false ↔ p ≠ s.startPos :=
  Decidable.not_iff_not.1 (by simp)

@[simp]
theorem isEmpty_slice {s : String} {p₁ p₂ h} : (s.slice p₁ p₂ h).isEmpty ↔ p₁ = p₂ := by
  simp [← Slice.startPos_eq_endPos_iff, ← Pos.ofSlice_inj]

@[simp]
theorem isEmpty_slice_eq_false_iff {s : String} {p₁ p₂ h} :
    (s.slice p₁ p₂ h).isEmpty = false ↔ p₁ ≠ p₂ := by
  rw [ne_eq, ← isEmpty_slice (h := h), Bool.not_eq_true]

@[simp]
theorem Slice.isEmpty_slice {s : Slice} {p₁ p₂ h} : (s.slice p₁ p₂ h).isEmpty ↔ p₁ = p₂ := by
  simp [← startPos_eq_endPos_iff, ← Pos.ofSlice_inj]

@[simp]
theorem Slice.isEmpty_slice_eq_false_iff {s : Slice} {p₁ p₂ h} :
    (s.slice p₁ p₂ h).isEmpty = false ↔ p₁ ≠ p₂ := by
  rw [ne_eq, ← isEmpty_slice (h := h), Bool.not_eq_true]

@[simp]
theorem toByteArray_eq_empty_iff {s : String} :
    s.toByteArray = ByteArray.empty ↔ s = "" := by
  simp [← toByteArray_inj]

theorem Slice.toByteArray_copy_eq_empty_iff {s : Slice} :
    s.copy.toByteArray = ByteArray.empty ↔ s.isEmpty = true := by
  simp

theorem Slice.toByteArray_copy_ne_empty_iff {s : Slice} :
    s.copy.toByteArray ≠ ByteArray.empty ↔ s.isEmpty = false := by
  simp

section CopyEqEmpty

-- Yes, `simp` can prove these, but we still need to mark them as simp lemmas.

@[simp]
theorem copy_slice_self {s : String} {p : s.Pos} : (s.slice p p (Pos.le_refl _)).copy = "" := by
  simp

@[simp]
theorem copy_sliceTo_startPos {s : String} : (s.sliceTo s.startPos).copy = "" := by
  simp

@[simp]
theorem copy_sliceFrom_startPos {s : String} : (s.sliceFrom s.endPos).copy = "" := by
  simp

@[simp]
theorem Slice.copy_slice_self {s : Slice} {p : s.Pos} : (s.slice p p (Pos.le_refl _)).copy = "" := by
  simp

@[simp]
theorem Slice.copy_sliceTo_startPos {s : Slice} : (s.sliceTo s.startPos).copy = "" := by
  simp

@[simp]
theorem Slice.copy_sliceFrom_endPos {s : Slice} : (s.sliceFrom s.endPos).copy = "" := by
  simp

end CopyEqEmpty

end String
