//
//  ViewController.swift
//  Sojourn
//
//  Created by Donald Dugger on 4/15/26.
//

import UIKit
import UniformTypeIdentifiers
import ConnectIQ

class TripVC: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource, UIPopoverPresentationControllerDelegate, PopupDelegate, TripDelegate, UIDocumentPickerDelegate, IQDeviceEventDelegate
{

var day_idx: Int = 0
var day_max: Int = 0

var nav_buttons: [UIBarButtonItem?]!
var more_but: UIBarButtonItem!
var day_but: UIBarButtonItem!
var event_but: UIBarButtonItem!

var popup: UIViewController!

var main_vcs: [ DayVC ] = []
var cur_vc: DayVC?

var json_str: String?

var file_read: Bool = false

func pageViewController(_ pageViewController: UIPageViewController, 
                        didFinishAnimating finished: Bool, 
                        previousViewControllers: [UIViewController], 
                        transitionCompleted completed: Bool)
{
    
    if completed {
        guard let vc = pageViewController.viewControllers?.first as? DayVC else {
            Log.l("TripVC:can't determine current view")
            return
        }
        cur_vc = vc
    }
}

func pageViewController(_ pageViewController: UIPageViewController,
                        viewControllerBefore viewController: UIViewController) -> UIViewController?
{

    guard let vc = viewController as? DayVC,
          let idx = vc.day_idx else {
        return nil
    }
    if idx <= 0 {
        return nil
    }
    return main_vcs[idx - 1]
}

func pageViewController(_ pageViewController: UIPageViewController,
                        viewControllerAfter viewController: UIViewController) -> UIViewController?
{

    guard let vc = viewController as? DayVC,
          let idx = vc.day_idx else {
        return nil
    }
    if idx >= (day_max - 1) {
        return nil
    }
    return main_vcs[idx + 1]
}

public func day_upd(_ day: Day)
{

//Log.l("TripVC: day_upd()")
    let i = C.itinerary.add_day(day)
    set_days(i)
    C.put_trip()
}

//  Called to create a new event
//
public func event_upd(old: Event?, new: Event)
{

Log.l("TripVC: event_upd()")

    cur_vc?.event_upd(old: old, new: new)
    C.put_trip()
}

func watch_upd(_ w_dev: IQDevice?, act: C.WATCH_ACT?)
{

//Log.l("watch_upd(\(w_dev?.friendlyName), \(act)")
    guard let dev = w_dev else {
        C.rm_persist("Garmin_devs")
        watch_act()
        return
    }
    let app = IQApp(uuid: UUID(uuidString: C.WATCH_UUID), store: UUID(), device: dev)
    if act == .START {
        ConnectIQ.sharedInstance()?
            .openAppRequest(app,
                         completion: {(r: IQSendMessageResult) -> Void in
                            if let msg = NSStringFromSendMessageResult(r) {
                              Log.l("TripVC:send => \(msg)") }
                         })
    } else if act == .SEND {
        ConnectIQ.sharedInstance()?
            .sendMessage(C.itinerary.marshall(),
                         to: app,
                         progress: nil,
                         completion: {(r: IQSendMessageResult) -> Void in
                            if let msg = NSStringFromSendMessageResult(r) {
                              Log.l("TripVC:send => \(msg)") }
                         })
    }
}

func trip_upd(_ wh: String, date: String, fmt: String)
{

//Log.l("TripVC: trip_upd() => \(wh), \(date), \(fmt)")
    let w = wh.isEmpty ? "Going somewhere?" : wh
    C.put_trip(fill_trip(w, date: date, fmt: fmt))
    C.app_restart()
}

public func json_upd(_ json: String)
{

//Log.l("TripVC: json_upd()")

    C.put_trip(json)
    C.app_restart()
}

func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL])
{

    guard let url = urls.first else { return }

    if file_read {
        let json = rd_file(url)
//Log.d("picker:json - \(json ?? "?")")
        if let str = json {
            C.itinerary = Itinerary(str)
            if C.itinerary.days.count > 0 {
                C.put_trip(str)
                C.app_restart()
            } else {
                C.alert("Load itinerary", msg: "Bad json string", vc: self)
            }
        } else {
            C.alert("Load itinerary", msg: "Bad json string", vc: self)
        }
    }
}

func set_days(_ day_idx: Int)
{

    if C.itinerary.days.count <= 0 {
        return }

    main_vcs = []
    day_max = C.itinerary.get_day_max()
    for i in 0..<day_max {
        add_vc(C.itinerary.get_day(i)!, idx: i) }
    self.day_idx = day_idx
    cur_vc = main_vcs[day_idx] as DayVC
    setViewControllers([cur_vc!], direction: .forward, animated: true, completion: nil)
}

private func add_vc(_ day: Day, idx: Int)
{
    
    let sb = UIStoryboard(name: "Main", bundle: nil)
    let vc = sb.instantiateViewController(withIdentifier: "DayVC") as! DayVC
    vc.delegate = self
    vc.day = day
    vc.day_idx = idx
    main_vcs.append(vc)
}

func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
    // Return no adaptive presentation style, use default presentation behaviour
    return .none
}

override
func viewDidLoad()
{
    var json: String? = nil

	super.viewDidLoad()
	// Do any additional setup after loading the view.
    if C.json_url != nil {
        Log.d("TripVC:viewDidLoad():json_url - \(C.json_url?.absoluteString ?? "")")
        let str = C.json_url?.absoluteString ?? ""
        if str.hasPrefix(C.GCM_PROTO) {
            watch_enum(C.json_url)
        } else {
            json = rd_file(C.json_url)
        }
        C.json_url = nil
    }
    if json == nil {
        json = C.get_trip() }
    else {
        C.put_trip(json!) }
    C.itinerary = Itinerary(json)
//Log.d("TripVC:\(C.itinerary.days.count), \(C.get_persist("Trip_preview") ?? "nil")")
    if (C.itinerary.days.count <= 0) ||
       ((C.get_persist("Trip_preview") as? String) == "yes") {
        json_str = json
    } else {
        json_str = nil
    }
//Log.d("itinerary:" + Json.json2str(C.itinerary.marshall())!)

    dataSource = self
    delegate = self

    set_navbar(C.itinerary.get_where() ?? "-??-")

    set_days(0);
}

override
func viewDidDisappear(_ animated: Bool)
{

    ConnectIQ.sharedInstance().unregister(forAllDeviceEvents: self)
}

override func viewDidAppear(_ animated: Bool)
{

    super.viewDidAppear(animated)
    if let json = json_str {
        json_act(json) }
}

func sh_popup(_ popVC: UIViewController, mode: UIModalPresentationStyle = .popover)
{

    popVC.modalPresentationStyle = mode
    popup = popVC
    let popOverVC = popVC.popoverPresentationController
    popOverVC?.delegate = self
    popOverVC?.sourceView = self.view
    popOverVC?.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
    popOverVC?.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0)
    present(popVC, animated: true)
}

public func rm_day(_ idx: Int)
{

//Log.l("TripVC: rm_day(\(idx))")
    let i = C.itinerary.rm_day(idx)
    if i < 0 {
        C.alert("Delete day", msg: "Can't remove last day", vc: self)
    } else {
        set_days(i)
    }
    C.put_trip()
}

@objc
func where_act(_ sender: UIButton)
{

//Log.d("where_act()")
    guard let popVC = storyboard?.instantiateViewController(withIdentifier: "PopupVC") as! PopupVC? else {
            return
    }
    popVC.delegate = self
    popVC.handler = WherePop()
    popVC.data = nil
    sh_popup(popVC)
}

func day_act()
{

//Log.d("day_act()")
    guard let popVC = storyboard?.instantiateViewController(withIdentifier: "PopupVC") as! PopupVC? else {
            return
    }
    popVC.delegate = self
    popVC.handler = NewDayPop()
    popVC.data = cur_vc!.day as Any?
    sh_popup(popVC)
}

func event_act()
{

//Log.d("event_act()")
    guard let popVC = storyboard?.instantiateViewController(withIdentifier: "PopupVC") as! PopupVC? else {
            return
    }
    popVC.delegate = self
    popVC.handler = EventPop()
    popVC.data = nil
    sh_popup(popVC)
}

func json_act(_ json: String)
{

//Log.d("json_act()")
    guard let popVC = storyboard?.instantiateViewController(withIdentifier: "PopupVC") as! PopupVC? else {
            return
    }
    popVC.delegate = self
    popVC.handler = JsonPop()
    popVC.data = json
    sh_popup(popVC)
}

func save_act()
{

//Log.d("save_act")
    guard let json = Json.json2str(C.itinerary.marshall()) else {
        C.alert("Save itinerary", msg: "JSON decode error", vc: self)
        return
    }
    file_read = false
    let fname = "itinerary.json"
    
    let furl = FileManager.default.temporaryDirectory.appendingPathComponent(fname)
    
    do {
        try json.write(to: furl, atomically: true, encoding: .utf8)
        let picker = UIDocumentPickerViewController(forExporting: [furl])
        picker.delegate = self
        picker.shouldShowFileExtensions = true
        self.present(picker, animated: true, completion: nil)
    } catch {
        Log.l("save_act: \(error)")
    }
}

func load_act()
{

//Log.d("load_act")
    file_read = true
    let picker = UIDocumentPickerViewController(forOpeningContentTypes: [.folder, .json], asCopy: true)
    picker.delegate = self
    self.present(picker, animated: true)
}

func trip_act()
{

//Log.d("trip_act")
    guard let popVC = storyboard?.instantiateViewController(withIdentifier: "PopupVC") as! PopupVC? else {
            return
    }
    popVC.delegate = self
    popVC.handler = NewTripPop()
    popVC.data = nil
    sh_popup(popVC)
}

func watch_act()
{

    let devs = C.get_persist("Garmin_devs")
//Log.d("watch_act(\(devs))")
    if let urls = devs {
        watch_enum(URL(string: urls as! String))
        return
    }

    if #available(iOS 18.0, *) {
        ConnectIQ.sharedInstance().showDeviceSelection() }
    else {
        watch_enum(URL(string: "sojourn-13579://device-select-resp?ciqApp=Connect&ciqBundle=com.garmin.connect.mobile&ciqScheme&ciqSdkVersion=10000&sourceUrlHost&d0ID=D5516A02-4C24-AAF0-CE61-C780FA34835D&d0Model=Venu%203&d0Name=Venu%203&d0PartNumber=006-B4260-00")) }
}

func iq_state(_ dev: IQDevice) -> String
{
    var state: String

    let s = ConnectIQ.sharedInstance().getDeviceStatus(dev)
    switch s {

    case .invalidDevice:
        state = "Invalid Device"

    case .bluetoothNotReady:
        state = "Bluetooth Off"

    case .notFound:
        state = "Not Found"

    case .notConnected:
        state = "Not Connected"

    case .connected:
        state = "Connected"

    default:
        state = "??"
    }
    return "\(state)(\(s.rawValue))"
}

func watch_enum(_ url: URL?)
{

    C.put_persist("Garmin_devs", val: url?.absoluteString)
    if let devs = ConnectIQ.sharedInstance().parseDeviceSelectionResponse(from: url), devs.count > 0 {
        for (_, dev) in devs.enumerated() {
            guard let dev = dev as? IQDevice else { continue }
//Log.l("watch_enum:\(dev.friendlyName ?? "-??-") => \(iq_state(dev))")
            ConnectIQ.sharedInstance().register(forDeviceEvents: dev, delegate: self)
        }

        guard let popVC = storyboard?.instantiateViewController(withIdentifier: "PopupVC") as! PopupVC? else {
                return
        }
        popVC.delegate = self
        popVC.handler = WatchPop()
        popVC.data = devs
        sh_popup(popVC)
    }
}

//func devicesChanged()
//{
//
//Log.l("devicesChanged()")
//}

func deviceStatusChanged(_ dev: IQDevice, status: IQDeviceStatus)
{
 
//Log.l("deviceStatusChanged:\(dev.friendlyName ?? "-??-") => \(iq_state(dev))")
}

func log_act()
{

//Log.d("log_menu")
    guard let popVC = storyboard?.instantiateViewController(withIdentifier: "LogVC") as! LogVC? else {
            return
    }
    popVC.delegate = self
    sh_popup(popVC, mode: .fullScreen)
}

func about_act()
{

//Log.d("about_menu")
    guard let popVC = storyboard?.instantiateViewController(withIdentifier: "AboutVC") as! AboutVC? else {
            return
    }
    popVC.delegate = self
    sh_popup(popVC)
}

func set_navbar(_ title: String)
{

    let l_but = UIBarButtonItem(title: title, style: .plain, target: self, action: #selector(where_act))
    navigationItem.leftBarButtonItem = l_but
    navigationItem.hidesBackButton = true


    let event_menu = UIAction(title: "New event") { _ in self.event_act() }
    let day_menu = UIAction(title: "New day") { _ in self.day_act() }
    let save_menu = UIAction(title: "Save") { _ in self.save_act() }
    let load_menu = UIAction(title: "Load") { _ in self.load_act() }
    let trip_menu = UIAction(title: "New trip") { _ in self.trip_act() }
    let watch_menu = UIAction(title: "Watch") { _ in self.watch_act() }
    let log_menu = UIAction(title: "Log") { _ in self.log_act() }
    let about_menu = UIAction(title: "About") { _ in self.about_act() }

    var items = [ event_menu, day_menu, save_menu, load_menu, trip_menu, watch_menu ]
    if false {
        items.append(log_menu) }
    items.append(about_menu)
    let more_menu = UIMenu(title: "More", children: items)

    navigationItem.rightBarButtonItems = []
    navigationItem.rightBarButtonItems?.append(UIBarButtonItem(title: "More", image: UIImage(systemName: "ellipsis"), primaryAction: nil, menu: more_menu))
}

func rd_file(_ url: URL?) -> String?
{

     do {
        if let f_url = url {
            let json = try String(contentsOf: f_url, encoding: .utf8)
            return json
        } else {
            return nil
        }
    } catch {
        Log.d("read error: \(String(describing: url))")
        return nil
    }
}

func fill_trip(_ wh: String, date: String, fmt: String) -> String
{
    return "{\"where\":\"\(wh)\"," +
             "\"date_fmt\":\"\(fmt)\"," +
             "\"days\":[{" +
                "\"date\":\"\(date)\"," +
                "\"events\":[]}]}"
}

}
