//
//  CharacterAudioSettingsView.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/31/23.
//

import SwiftUI
import AVFoundation

struct CharacterAudioSettingsView: View {
    @Environment(\.presentationMode) var presentationMode
    @Binding var play: Play
    @State private var selectedVoice: [AVSpeechSynthesisVoice] = []
    @State private var speechRate: [Float] = []

    var body: some View {
        NavigationView {
            List {
                ForEach(Array(play.characters.enumerated()), id: \.element.id) { index, character in
                    VStack(alignment: .leading, spacing: 10) {
                        Text(character.name)
                            .font(.headline)

                        HStack {
                            Text("Voice:")
                            Picker("Voice", selection: $selectedVoice[index]) {
                                ForEach(AVSpeechSynthesisVoice.speechVoices(), id: \.self) { voice in
                                    Text(voice.name).tag(voice)
                                }
                            }
                            .pickerStyle(MenuPickerStyle())
                        }

                        HStack {
                            Text("Speech Rate:")
                            Slider(value: $speechRate[index], in: AVSpeechUtteranceMinimumSpeechRate...AVSpeechUtteranceMaximumSpeechRate)
                        }
                    }
                }
            }
            .navigationTitle("Character Audio Settings")
            .navigationBarItems(trailing: Button(action: {
                presentationMode.wrappedValue.dismiss()
            }) {
                Text("Done")
            })
            .onAppear {
                initializeSettings()
            }
        }
    }
    
    func initializeSettings() {
        for _ in play.characters {
            selectedVoice.append(AVSpeechSynthesisVoice(language: "en-US")!)
            speechRate.append(AVSpeechUtteranceDefaultSpeechRate)
        }
    }
}
//
//  CharacterImportView.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/31/23.
//
import SwiftUI

struct CharacterImportView: View {
    @Environment(\.presentationMode) var presentationMode
    @Binding var characters: [TheaterCharacter]
    
    @State private var characterNames: [String] = []
    @State private var importMethod = ImportMethod.camera
    @State private var showAlert = false
    @State private var alertTitle = ""
    @State private var alertMessage = ""
    
    var body: some View {
        NavigationView {
            VStack {
                Form {
                    Picker("Import Method", selection: $importMethod) {
                        Text("Camera").tag(ImportMethod.camera)
                        Text("Photo Library").tag(ImportMethod.photoLibrary)
                        Text("PDF").tag(ImportMethod.pdf)
                    }
                }
                
                Button(action: {
                    importCharacters()
                }) {
                    Text("Import Characters")
                }
                .padding()
            }
            .navigationTitle("Import Characters")
            .navigationBarItems(trailing: Button("Done") {
                presentationMode.wrappedValue.dismiss()
            })
            .alert(isPresented: $showAlert) {
                Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text("OK")))
            }
        }
    }
    
    private func importCharacters() {
        switch importMethod {
        case .camera:
            // Camera import logic will be added here.
            break
        case .photoLibrary:
            // Photo library import logic will be added here.
            break
        case .pdf:
            // PDF import logic will be added here.
            break
        }
    }

    private func processCharacterList(_ characterList: String) {
        let lines = characterList.split(separator: "\n").map(String.init)
        
        for line in lines {
            let name = line.trimmingCharacters(in: .whitespacesAndNewlines)
            if !name.isEmpty {
                // Add default values for voiceLanguage and voiceRate
                let character = TheaterCharacter(id: UUID(), name: name, voiceLanguage: "en-US", voiceRate: 0.5)
                characters.append(character)
            }
        }
    }


    private enum ImportMethod: Int, CaseIterable, Identifiable {
        case camera
        case photoLibrary
        case pdf

        var id: Int { rawValue }
    }
}
//
//  CustomScriptImportView.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/31/23.
//
import SwiftUI
import PhotosUI
import Vision
import Foundation

class CustomScriptStorage: ObservableObject {
    @Published var scripts: [Script] = []
}

struct CustomScriptImportView: View {
    @EnvironmentObject var scriptStorage: CustomScriptStorage
    @State private var showCharacterSelection = false
    @State private var showImagePicker = false
    @State private var script: Script = Script(id: UUID(), title: "", characters: [], dialogues: [])
    @State private var showAlert = false
    @State private var alertTitle = ""
    @State private var alertMessage = ""
    @State private var isProcessing = false
    @State private var selectedImage: UIImage?
    
    var body: some View {
        NavigationView {
            VStack {
                Text("Import Your Script")
                    .font(.title)
                    .padding()

                Button(action: {
                    showImagePicker = true
                }) {
                    Text("Import from Photo Library")
                        .padding()
                        .background(Color.blue)
                        .foregroundColor(.white)
                        .cornerRadius(8)
                }
                .sheet(isPresented: $showImagePicker) {
                    ImagePicker(selectedImage: $selectedImage)
                }
                .onChange(of: selectedImage) { newImage in
                    guard let image = newImage else { return }
                    isProcessing = true
                    performOCR(on: image) { recognizedText in
                        isProcessing = false
                        guard let recognizedText = recognizedText else {
                            showAlert(title: "Error", message: "Failed to recognize text")
                            return
                        }
                        processScript(recognizedText)
                    }
                }
            }
            .padding()
            .navigationBarTitle("Script Import", displayMode: .inline)
            .alert(isPresented: $showAlert) {
                Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text("OK")))
            }
            .overlay(isProcessing ? ProgressView("Processing...") : nil)
        }
    }

    private func showAlert(title: String, message: String) {
        alertTitle = title
        alertMessage = message
        showAlert = true
    }

    private func performOCR(on image: UIImage, completion: @escaping (String?) -> Void) {
        guard let cgImage = image.cgImage else {
            completion(nil)
            return
        }

        let requestHandler = VNImageRequestHandler(cgImage: cgImage, options: [:])
        let request = VNRecognizeTextRequest { request, error in
            if let error = error {
                print("Error recognizing text: \(error)")
                completion(nil)
                return
            }
            guard let observations = request.results as? [VNRecognizedTextObservation] else {
                completion(nil)
                return
            }
            let recognizedText = observations.compactMap { observation in
                observation.topCandidates(1).first?.string
            }.joined(separator: "\n")

            completion(recognizedText)
        }
        request.recognitionLevel = .accurate

        do {
            try requestHandler.perform([request])
        } catch {
            print("Error performing OCR: \(error)")
            completion(nil)
        }
    }
    private func processScript(_ recognizedText: String) {
        let lines = recognizedText.split(separator: "\n")

        var currentCharacter: TheaterCharacter?
        var dialogues: [Dialogue] = []

        for line in lines {
            let trimmedLine = line.trimmingCharacters(in: .whitespacesAndNewlines)
            if trimmedLine.isEmpty { continue }

            let newCharacter = TheaterCharacter(id: UUID(), name: trimmedLine, voiceLanguage: "en-US", voiceRate: 0.5)
            if let existingCharacter = script.characters.first(where: { $0.name == newCharacter.name }) {
                currentCharacter = existingCharacter
            } else if let currentCharacter = currentCharacter {
                dialogues.append(Dialogue(id: UUID(), characterID: currentCharacter.id, content: String(line)))
            }
        }


        let script = Script(id: UUID(), title: "Untitled", characters: [], dialogues: dialogues)

        scriptStorage.scripts.append(script)
        isProcessing = false
        showAlert(title: "Success", message: "Script successfully imported!")
    }
}

struct CustomScriptImportView_Previews: PreviewProvider {
    static var previews: some View {
        CustomScriptImportView().environmentObject(CustomScriptStorage())
    }
}
//
//  DialogueImportView.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/31/23.
//

import SwiftUI

struct DialogueImportView: View {
    @Environment(\.presentationMode) private var presentationMode
    @ObservedObject var play: Play
    @State private var importMethod: ImportMethod = .camera
    @State private var dialogues: [Dialogue] = []

    var body: some View {
        VStack {
            Text("Import Dialogue")
                .font(.largeTitle)
                .padding()
            
            Picker("Import Method", selection: $importMethod) {
                ForEach(ImportMethod.allCases) { method in
                    Text(method.description).tag(method)
                }
            }
            .pickerStyle(SegmentedPickerStyle())
            .padding()

            Button(action: importDialogues) {
                Text("Import")
                    .font(.title)
                    .padding()
            }
            .disabled(dialogues.isEmpty)

            Spacer()
        }
    }
}

extension DialogueImportView {
    enum ImportMethod: String, CaseIterable, Identifiable {
        case camera
        case photoLibrary
        case pdf

        var id: String { rawValue }
        var description: String {
            switch self {
            case .camera:
                return "Camera"
            case .photoLibrary:
                return "Photo Library"
            case .pdf:
                return "PDF"
            }
        }
    }

    private func importDialogues() {
        switch importMethod {
        case .camera:
            // Add camera functionality for importing dialogues
            break
        case .photoLibrary:
            // Add photo library functionality for importing dialogues
            break
        case .pdf:
            // Add PDF importing functionality for importing dialogues
            break
        }
    }
}

struct DialogueImportView_Previews: PreviewProvider {
    static var previews: some View {
        DialogueImportView(play: Play.example)
    }
}
//
//  ImagePicker.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 4/4/23.
//

import SwiftUI
import PhotosUI

struct ImagePicker: UIViewControllerRepresentable {
    @Binding var selectedImage: UIImage?

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: Context) -> PHPickerViewController {
        var config = PHPickerConfiguration()
        config.filter = .images
        config.selectionLimit = 1
        let controller = PHPickerViewController(configuration: config)
        controller.delegate = context.coordinator
        return controller
    }

    func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {
    }

    class Coordinator: NSObject, PHPickerViewControllerDelegate {
        let parent: ImagePicker

        init(_ parent: ImagePicker) {
            self.parent = parent
        }

        func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
            picker.dismiss(animated: true)

            guard let result = results.first else {
                return
            }

            let itemProvider = result.itemProvider

            if itemProvider.canLoadObject(ofClass: UIImage.self) {
                itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
                    DispatchQueue.main.async {
                        if let image = image as? UIImage {
                            self?.parent.selectedImage = image
                        } else {
                            print("Failed to load image:", error?.localizedDescription ?? "unknown error")
                        }
                    }
                }
            }
        }
    }
}
//
//  NewPlayView.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/31/23.
//

import SwiftUI

struct NewPlayView: View {
    @EnvironmentObject var playStorage: PlayStorage
    @Environment(\.presentationMode) var presentationMode
    @State private var playTitle: String = ""

    var body: some View {
        NavigationView {
            VStack {
                TextField("Enter play title", text: $playTitle)
                    .padding()

                Button(action: {
                    let newPlay = Play(id: UUID(), title: playTitle, acts: [], characters: [])
                    playStorage.plays.append(newPlay)
                    presentationMode.wrappedValue.dismiss()
                }) {
                    Text("Add Play")
                        .font(.title2)
                        .padding()
                }
            }
            .padding()
            .navigationBarTitle("New Play", displayMode: .inline)
        }
    }
}
//
//  Persistence.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/22/23.
//

import CoreData

class PersistenceController: ObservableObject {
    static let shared = PersistenceController()

    static var preview: PersistenceController = {
        let result = PersistenceController(inMemory: true)
        let viewContext = result.container.viewContext
        for _ in 0..<10 {
            let newItem = Item(context: viewContext)
            newItem.timestamp = Date()
        }
        do {
            try viewContext.save()
        } catch {
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
        return result
    }()

    let container: NSPersistentCloudKitContainer

    init(inMemory: Bool = false) {
        container = NSPersistentCloudKitContainer(name: "ThatsYourCue")
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        container.viewContext.automaticallyMergesChangesFromParent = true
    }
}
//
//  Play.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/31/23.
//

import Foundation

class Play: Identifiable, Codable, ObservableObject {
    let id: UUID
    let title: String
    let acts: [Act]
    let characters: [TheaterCharacter]
    
    init(id: UUID, title: String, acts: [Act], characters: [TheaterCharacter]) {
        self.id = id
        self.title = title
        self.acts = acts
        self.characters = characters
    }
    
    // Add this function inside the Play class
    func character(withId id: UUID) -> TheaterCharacter? {
        return self.characters.first { $0.id == id }
    }

    // Add this function inside the Play class
    func allCharacterLines() -> [(UUID, String)] {
        var lines: [(UUID, String)] = []

        for act in self.acts {
            for scene in act.scenes {
                for dialogue in scene.dialogues {
                    lines.append((dialogue.characterID, dialogue.content))
                }
            }
        }

        return lines
    }
}

struct Act: Identifiable, Codable {
    let id: UUID
    let number: Int
    let scenes: [Scene]
}

struct Scene: Identifiable, Codable {
    let id: UUID
    let number: Int
    let dialogues: [Dialogue]
}

struct Dialogue: Identifiable, Codable {
    let id: UUID
    let characterID: UUID
    let content: String
}

extension Play {
    static func exampleData() -> [Play] {
        let exampleCharacters = TheaterCharacter.exampleData()
        return [
            Play(id: UUID(), title: "Play 1", acts: [], characters: exampleCharacters),
            Play(id: UUID(), title: "Play 2", acts: [], characters: exampleCharacters),
            Play(id: UUID(), title: "Play 3", acts: [], characters: exampleCharacters)
        ]
    }
    
    static let example = Play(id: UUID(), title: "Example Play", acts: [], characters: TheaterCharacter.exampleData())
}
//
//  PlayCreationView.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/31/23.
//

import SwiftUI

struct PlayCreationView: View {
    @EnvironmentObject var playStorage: PlayStorage
    @Environment(\.presentationMode) var presentationMode
    @State private var playTitle: String
    @State private var characters: [TheaterCharacter]
    @State private var isImportingCharacters = false
    
    init(play: Play? = nil) {
        _playTitle = State(initialValue: play?.title ?? "")
        _characters = State(initialValue: play?.characters ?? [])
    }

    func savePlay() {
        let newPlay = Play(id: UUID(), title: playTitle, acts: [], characters: characters)
        playStorage.plays.append(newPlay)
        presentationMode.wrappedValue.dismiss()
    }

    var body: some View {
        NavigationView {
            VStack {
                Form {
                    TextField("Enter play title", text: $playTitle)

                    Section(header: Text("Characters")) {
                        ForEach(characters) { character in
                            Text(character.name)
                        }
                        Button(action: {
                            isImportingCharacters.toggle()
                        }) {
                            HStack {
                                Image(systemName: "plus")
                                Text("Import Characters")
                            }
                        }
                    }
                }
                Spacer()
            }
            .navigationBarTitle("New Play", displayMode: .inline)
            .navigationBarItems(trailing: Button("Save", action: savePlay))
            .sheet(isPresented: $isImportingCharacters) {
                CharacterImportView(characters: $characters)
            }
        }
    }
}

struct PlayCreationView_Previews: PreviewProvider {
    static var previews: some View {
        PlayCreationView().environmentObject(PlayStorage())
    }
}
//
//  PlayDetailsView.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/31/23.
//

import SwiftUI

struct PlayDetailsView: View {
    @EnvironmentObject var playStorage: PlayStorage
    @ObservedObject var play: Play
    @State private var showEditPlayView = false
    
    var body: some View {
        ScrollView {
            VStack(alignment: .leading, spacing: 20) {
                Text(play.title)
                    .font(.largeTitle)
                    .fontWeight(.bold)
                
                VStack(alignment: .leading, spacing: 10) {
                    Text("Characters")
                        .font(.title2)
                        .fontWeight(.bold)
                    
                    ForEach(play.characters) { character in
                        Text(character.name)
                            .font(.body)
                    }
                }
                
                // Display acts, scenes, and dialogues here
            }
            .padding()
        }
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button(action: {
                    showEditPlayView = true
                }) {
                    Image(systemName: "pencil")
                }
            }
        }
        .sheet(isPresented: $showEditPlayView) {
            PlayCreationView(play: play)
                .environmentObject(playStorage)
        }
    }
}

struct PlayDetailsView_Previews: PreviewProvider {
    static var previews: some View {
        PlayDetailsView(play: Play.exampleData().first!)
            .environmentObject(PlayStorage())
    }
}
//
//  PlayListView.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/31/23.
//

import SwiftUI

struct PlayListView: View {
    @EnvironmentObject var playStorage: PlayStorage
    @State private var isAddingNewPlay = false
    
    var body: some View {
        NavigationView {
            List {
                ForEach(playStorage.plays) { play in
                    NavigationLink(destination: PlayDetailsView(play: play)) {
                        Text(play.title)
                    }
                }
                .onDelete(perform: deletePlay)
            }
            .navigationBarTitle("Plays")
            .navigationBarItems(
                leading: EditButton(),
                trailing: Button(action: {
                    isAddingNewPlay.toggle()
                }) {
                    Image(systemName: "plus")
                }
            )
            .sheet(isPresented: $isAddingNewPlay) {
                NewPlayView().environmentObject(playStorage)
            }
        }
    }

    private func deletePlay(at offsets: IndexSet) {
        playStorage.plays.remove(atOffsets: offsets)
    }
}
//
//  PlayStorage.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/31/23.
//

import SwiftUI
import Combine

class PlayStorage: ObservableObject {
    @Published var plays: [Play] = []

    private let saveKey = "SavedPlays"
    private var autosave: AnyCancellable?

    init() {
        loadPlays()
        autosave = $plays.sink { plays in
            let encoder = JSONEncoder()
            if let encodedPlays = try? encoder.encode(plays) {
                UserDefaults.standard.set(encodedPlays, forKey: self.saveKey)
            }
        }
    }

    private func loadPlays() {
        if let savedData = UserDefaults.standard.data(forKey: saveKey) {
            let decoder = JSONDecoder()
            if let decodedPlays = try? decoder.decode([Play].self, from: savedData) {
                self.plays = decodedPlays
                return
            }
        }
        self.plays = []
    }
}
//
//  RecordingView.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/31/23.
//

import SwiftUI
import AVFoundation

struct RecordingView: View {
    @Environment(\.presentationMode) var presentationMode
    @Binding var play: Play
    @State private var isRecording = false
    @State private var audioRecorder: AVAudioRecorder?
    @State private var characterLines: [(UUID, String)] = []

    private var playValue: Play {
        get { play }
        set { play = newValue }
    }

    var body: some View {
        VStack {
            ScrollView {
                VStack(alignment: .leading, spacing: 10) {
                    ForEach(characterLines, id: \.0) { characterID, line in
                        if let character = playValue.character(withId: characterID) {
                            Text("\(character.name): \(line)")
                                .font(.body)
                        }
                    }
                }
            }
            .padding(.horizontal)

            HStack {
                Button(action: {
                    if isRecording {
                        stopRecording()
                    } else {
                        startRecording()
                    }
                }) {
                    Text(isRecording ? "Stop Recording" : "Start Recording")
                        .font(.title2)
                        .foregroundColor(.white)
                        .frame(maxWidth: .infinity)
                        .padding()
                        .background(isRecording ? Color.red : Color.blue)
                        .cornerRadius(10)
                        .padding(.horizontal)
                }
            }
        }
        .navigationBarTitle("Recording", displayMode: .inline)
        .navigationBarItems(trailing: Button(action: {
            presentationMode.wrappedValue.dismiss()
        }) {
            Text("Done")
        })
        .onAppear {
            prepareCharacterLines()
            setupAudioRecorder()
        }
    }
    
    private func prepareCharacterLines() {
        characterLines = playValue.allCharacterLines()
    }

    private func setupAudioRecorder() {
        let audioSession = AVAudioSession.sharedInstance()

        do {
            try audioSession.setCategory(.playAndRecord, mode: .default)
            try audioSession.setActive(true)

            let recordingSettings = [
                AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
                AVSampleRateKey: 12000,
                AVNumberOfChannelsKey: 1,
                AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
            ]

            audioRecorder = try AVAudioRecorder(url: getDocumentsDirectory().appendingPathComponent("recording.m4a"), settings: recordingSettings)
            audioRecorder?.prepareToRecord()
        } catch {
            print("Failed to set up audio recorder: \(error.localizedDescription)")
        }
    }

    private func startRecording() {
        isRecording = true
        audioRecorder?.record()
    }

    private func stopRecording() {
        isRecording = false
        audioRecorder?.stop()
    }

    private func getDocumentsDirectory() -> URL {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0]
    }
}

struct RecordingView_Previews: PreviewProvider {
    static var previews: some View {
        RecordingView(play: .constant(Play.example))
    }
}
//
//  RehearsalView.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/31/23.
//

import SwiftUI
import AVFoundation

struct CharacterLine {
    let character: TheaterCharacter
    let content: String
}

struct RehearsalView: View {
    @Binding var play: Play
    @State private var isPlaying = false
    @State private var characterLines: [CharacterLine] = []
    
    private var speechSynthesizer = AVSpeechSynthesizer()
    private let speechDelegate = SpeechDelegate()
    
    var body: some View {
        VStack {
            Text("Rehearsal")
                .font(.largeTitle)
                .padding()
            
            if isPlaying {
                Button(action: {
                    stopRehearsal()
                }) {
                    Text("Stop Rehearsal")
                        .font(.title)
                        .padding()
                }
            } else {
                Button(action: {
                    startRehearsal()
                }) {
                    Text("Start Rehearsal")
                        .font(.title)
                        .padding()
                }
            }
        }
        .onAppear {
            prepareCharacterLines()
            speechSynthesizer.delegate = speechDelegate
        }
    }
    
    // MARK: - Rehearsal Functions
    func prepareCharacterLines() {
        characterLines = []

        for act in play.acts {
            for scene in act.scenes {
                for dialogue in scene.dialogues {
                    if let character = play.character(withId: dialogue.characterID) {
                        characterLines.append(CharacterLine(character: character, content: dialogue.content))
                    }
                }
            }
        }
    }

    func startRehearsal() {
        isPlaying = true
        playCharacterLines(characterLines)
    }

    func stopRehearsal() {
        isPlaying = false
        speechSynthesizer.stopSpeaking(at: .immediate)
    }

    func playCharacterLines(_ lines: [CharacterLine]) {
        guard !lines.isEmpty else {
            isPlaying = false
            return
        }

        let characterLine = lines[0]
        let utterance = AVSpeechUtterance(speechString: characterLine.content)
        utterance.voice = AVSpeechSynthesisVoice(language: characterLine.character.voiceLanguage)
        utterance.rate = characterLine.character.voiceRate

        speechSynthesizer.speak(utterance)
        let estimatedSpeechDuration = utterance.estimatedSpeechDuration

        DispatchQueue.main.asyncAfter(deadline: .now() + estimatedSpeechDuration) {
            playCharacterLines(Array(lines.dropFirst()))
        }
    }
}

class SpeechDelegate: NSObject, AVSpeechSynthesizerDelegate {
    func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
        // Add any required functionality when speech finishes
    }
}

extension AVSpeechUtterance {
    private struct AssociatedKeys {
        static var speechString = "speechString"
    }
    
    convenience init(speechString: String) {
        self.init(string: speechString)
        objc_setAssociatedObject(self, &AssociatedKeys.speechString, speechString, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
    
    var speechString: String {
        return objc_getAssociatedObject(self, &AssociatedKeys.speechString) as? String ?? ""
    }
    
    var estimatedSpeechDuration: TimeInterval {
        let wordsPerMinute = 160.0
        let words = self.speechString.split(separator: " ").count
        let minutes = Double(words) / wordsPerMinute
        return minutes * 60.0
    }
}
//
//  Script.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/31/23.
//

import Foundation

struct Script: Identifiable {
    let id: UUID
    let title: String
    let characters: [TheaterCharacter]
    let dialogues: [Dialogue]
}

//
//  ScriptImportView.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/31/23.
//

import SwiftUI
import PhotosUI
import Vision
import Foundation

class CustomScriptStorage: ObservableObject {
    @Published var scripts: [Script] = []
}

struct CustomScriptImportView: View {
    @EnvironmentObject var scriptStorage: CustomScriptStorage
    @State private var showCharacterSelection = false
    @State private var showImagePicker = false
    @State private var script: Script = Script(id: UUID(), title: "", characters: [], dialogues: [])
    @State private var showAlert = false
    @State private var alertTitle = ""
    @State private var alertMessage = ""
    @State private var isProcessing = false
    @State private var selectedImage: UIImage?

    var body: some View {
        NavigationView {
            VStack {
                Text("Import Your Script")
                    .font(.title)
                    .padding()

                Button(action: {
                    showImagePicker = true
                }) {
                    Text("Import from Photo Library")
                        .padding()
                        .background(Color.blue)
                        .foregroundColor(.white)
                        .cornerRadius(8)
                }
                .sheet(isPresented: $showImagePicker) {
                    ImagePicker(sourceType: .photoLibrary, selectedImage: $selectedImage)
                }
                .onChange(of: selectedImage) { newImage in
                    guard let image = newImage else { return }
                    isProcessing = true
                    performOCR(on: image) { recognizedText in
                        isProcessing = false
                        guard let recognizedText = recognizedText else {
                            showAlert(title: "Error", message: "Failed to recognize text")
                            return
                        }
                        processScript(recognizedText)
                    }
                }
            }
            .padding()
            .navigationBarTitle("Script Import", displayMode: .inline)
            .alert(isPresented: $showAlert) {
                Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text("OK")))
            }
            .overlay(isProcessing ? ProgressView("Processing...") : nil)
        }
    }

    private func characterFromName(_ name: String) -> Character? {
        for character in Character.allCases {
            if character.name == name {
                return character
            }
        }
        return nil
    }
    
    private func showAlert(title: String, message: String) {
        alertTitle = title
        alertMessage = message
        showAlert = true
    }

    private func performOCR(on image: UIImage, completion: @escaping (String?) -> Void) {
        guard let cgImage = image.cgImage else {
            completion(nil)
            return
        }

        let requestHandler = VNImageRequestHandler(cgImage: cgImage, options: [:])
        let request = VNRecognizeTextRequest { request, error in
            if let error = error {
                print("Error recognizing text: \(error)")
                completion(nil)
                return
            }

            guard let observations = request.results as? [VNRecognizedTextObservation] else {
                completion(nil)
                return
            }

            let recognizedText = observations.compactMap { observation in
                observation.topCandidates(1).first?.string
            }.joined(separator: "\n")

            completion(recognizedText)
        }
        request.recognitionLevel = .accurate

        do {
            try requestHandler.perform([request])
        } catch {
            print("Error performing OCR: \(error)")
            completion(nil)
        }
    }

    private func processScript(_ recognizedText: String) {
        let lines = recognizedText.split(separator: "\n")

        var currentCharacter: Character?
        var dialogues: [Dialogue] = []

        for line in lines {
            let trimmedLine = line.trimmingCharacters(in: .whitespacesAndNewlines)
            if trimmedLine.isEmpty { continue }

            if let character = characterFromName(trimmedLine) {
                currentCharacter = character
            } else if let currentCharacter = currentCharacter {
                dialogues.append(Dialogue(id: UUID(), characterID: currentCharacter.id, content: String(line)))
            }
        }
        let script = Script(id: UUID(), title: "Untitled", characters: [], dialogues: dialogues)

        scriptStorage.scripts.append(script)
        isProcessing = false
        showAlert(title: "Success", message: "Script successfully imported!")
    }
}

struct CustomScriptImportView_Previews: PreviewProvider {
    static var previews: some View {
        CustomScriptImportView().environmentObject(CustomScriptStorage())
    }
}

//
//  ThatsYourCueApp.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/22/23.
//
import SwiftUI

@main
struct ThatsYourCueApp: App {
    var body: some Scene {
        WindowGroup {
            PlayListView()
        }
    }
}
//
//  Character.swift
//  ThatsYourCue
//
//  Created by Daniel Shearon on 3/31/23.
//

import Foundation

struct TheaterCharacter: Identifiable, Codable {
    let id: UUID
    let name: String
    let voiceLanguage: String
    let voiceRate: Float
}

extension TheaterCharacter {
    static func exampleData() -> [TheaterCharacter] {
        return [
            TheaterCharacter(id: UUID(), name: "Character 1", voiceLanguage: "en-US", voiceRate: 0.5),
            TheaterCharacter(id: UUID(), name: "Character 2", voiceLanguage: "en-US", voiceRate: 0.5),
            TheaterCharacter(id: UUID(), name: "Character 3", voiceLanguage: "en-US", voiceRate: 0.5)
        ]
    }
}