// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.

// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity.  If not, see <http://www.gnu.org/licenses/>.

import Cocoa

@NSApplicationMain
@available(macOS, deprecated: 10.11)

class AppDelegate: NSObject, NSApplicationDelegate {
	@IBOutlet weak var statusMenu: NSMenu!
	@IBOutlet weak var startAtLogonMenuItem: NSMenuItem!
	
	let statusItem = NSStatusBar.system().statusItem(withLength: NSVariableStatusItemLength)
	var parityPid: Int32? = nil
	var commandLine: [String] = []
	let defaultConfig = "[network]\nwarp = true"
	let defaultDefaults = "{\"fat_db\":false,\"mode\":\"passive\",\"mode.alarm\":3600,\"mode.timeout\":300,\"pruning\":\"fast\",\"tracing\":false}"
	
	func menuAppPath() -> String {
		return Bundle.main.executablePath!
	}

	func parityPath() -> String {
		return Bundle.main.bundlePath + "/Contents/MacOS/parity"
	}

	func isAlreadyRunning() -> Bool {
		return NSRunningApplication.runningApplications(withBundleIdentifier: Bundle.main.bundleIdentifier!).count > 1

	}
	
	func isParityRunning() -> Bool {
		if let pid = self.parityPid {
			return kill(pid, 0) == 0
		}
		return false
	}
	
	func killParity() {
		if let pid = self.parityPid {
			kill(pid, SIGINT)
		}
	}
	
	func openUI() {
		let parity = Process()
		parity.launchPath = self.parityPath()
		parity.arguments = self.commandLine
		parity.arguments!.append("ui")
		parity.launch()
	}
	
	func writeConfigFiles() {
		let basePath = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first?
			.appendingPathComponent(Bundle.main.bundleIdentifier!, isDirectory: true)
		
		if FileManager.default.fileExists(atPath: basePath!.path) {
			return
		}
		
		do {
			let defaultsFileDir = basePath?.appendingPathComponent("chains").appendingPathComponent("ethereum")
			let defaultsFile = defaultsFileDir?.appendingPathComponent("user_defaults")
			
			try FileManager.default.createDirectory(atPath: (defaultsFileDir?.path)!, withIntermediateDirectories: true, attributes: nil)
			if !FileManager.default.fileExists(atPath: defaultsFile!.path) {
				try defaultDefaults.write(to: defaultsFile!, atomically: false, encoding: String.Encoding.utf8)
			}
		
			let configFile = basePath?.appendingPathComponent("config.toml")
			if !FileManager.default.fileExists(atPath: configFile!.path) {
				try defaultConfig.write(to: configFile!, atomically: false, encoding: String.Encoding.utf8)
			}
		}
		catch {}
	}
	
	func autostartEnabled() -> Bool {
		return itemReferencesInLoginItems().existingReference != nil
	}

	func itemReferencesInLoginItems() -> (existingReference: LSSharedFileListItem?, lastReference: LSSharedFileListItem?) {
		let itemUrl: UnsafeMutablePointer<Unmanaged<CFURL>?> = UnsafeMutablePointer<Unmanaged<CFURL>?>.allocate(capacity: 1)
		if let appUrl: NSURL = NSURL.fileURL(withPath: Bundle.main.bundlePath) as NSURL? {
			let loginItemsRef = LSSharedFileListCreate(
				nil,
				kLSSharedFileListSessionLoginItems.takeRetainedValue(),
				nil
				).takeRetainedValue() as LSSharedFileList?
			if loginItemsRef != nil {
				let loginItems: NSArray = LSSharedFileListCopySnapshot(loginItemsRef, nil).takeRetainedValue() as NSArray
				if(loginItems.count > 0)
				{
					let lastItemRef: LSSharedFileListItem = loginItems.lastObject as! LSSharedFileListItem
					for i in 0 ..< loginItems.count {
						let currentItemRef: LSSharedFileListItem = loginItems.object(at: i) as! LSSharedFileListItem
						if LSSharedFileListItemResolve(currentItemRef, 0, itemUrl, nil) == noErr {
							if let urlRef: NSURL =  itemUrl.pointee?.takeRetainedValue() {
								if urlRef.isEqual(appUrl) {
									return (currentItemRef, lastItemRef)
								}
							}
						}
					}
					//The application was not found in the startup list
					return (nil, lastItemRef)
				}
				else
				{
					let addAtStart: LSSharedFileListItem = kLSSharedFileListItemBeforeFirst.takeRetainedValue()
					return(nil, addAtStart)
				}
			}
		}
		return (nil, nil)
	}
	
	func toggleLaunchAtStartup() {
		let itemReferences = itemReferencesInLoginItems()
		let shouldBeToggled = (itemReferences.existingReference == nil)
		let loginItemsRef = LSSharedFileListCreate(
			nil,
			kLSSharedFileListSessionLoginItems.takeRetainedValue(),
			nil
			).takeRetainedValue() as LSSharedFileList?
		if loginItemsRef != nil {
			if shouldBeToggled {
				if let appUrl : CFURL = NSURL.fileURL(withPath: Bundle.main.bundlePath) as CFURL? {
					LSSharedFileListInsertItemURL(
						loginItemsRef,
						itemReferences.lastReference,
						nil,
						nil,
						appUrl,
						nil,
						nil
					)
				}
			} else {
				if let itemRef = itemReferences.existingReference {
					LSSharedFileListItemRemove(loginItemsRef,itemRef)
				}
			}
		}
	}

	func launchParity() {
		self.commandLine = CommandLine.arguments.dropFirst().filter({ $0 != "ui"})
		
		let processes = GetBSDProcessList()!
		let parityProcess = processes.index(where: {
			var name = $0.kp_proc.p_comm
			let str = withUnsafePointer(to: &name) {
				$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: name)) {
				String(cString: $0)
				}
			}
			return str == "parity"
		})
		
		if parityProcess == nil {
			let parity = Process()
			let p = self.parityPath()
			parity.launchPath = p//self.parityPath()
			parity.arguments = self.commandLine
			parity.launch()
			self.parityPid = parity.processIdentifier
		} else {
			self.parityPid = processes[parityProcess!].kp_proc.p_pid
		}
	}
	
	func applicationDidFinishLaunching(_ aNotification: Notification) {
		if self.isAlreadyRunning() {
			openUI()
			NSApplication.shared().terminate(self)
			return
		}

		self.writeConfigFiles()
		self.launchParity()
		Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: {_ in 
			if !self.isParityRunning() {
				NSApplication.shared().terminate(self)
			}
		})
		
		let icon = NSImage(named: "statusIcon")
		icon?.isTemplate = true // best for dark mode
		statusItem.image = icon
		statusItem.menu = statusMenu
	}

	override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
		if menuItem == self.startAtLogonMenuItem! {
			menuItem.state = self.autostartEnabled() ? NSOnState : NSOffState
		}
		return true
	}
	
	@IBAction func quitClicked(_ sender: NSMenuItem) {
		self.killParity()
		NSApplication.shared().terminate(self)
	}
	
	@IBAction func openClicked(_ sender: NSMenuItem) {
		self.openUI()
	}
	
	@IBAction func startAtLogonClicked(_ sender: NSMenuItem) {
		self.toggleLaunchAtStartup()
	}

}