from gosubl import about
from gosubl import gs
from gosubl import gsq
from gosubl import gsshell
from gosubl import mg9
from gosubl import sh
import datetime
import json
import os
import re
import shlex
import string
import sublime
import sublime_plugin
import uuid
import webbrowser

DOMAIN = "9o"
AC_OPTS = sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS
SPLIT_FN_POS_PAT = re.compile(r'(.+?)(?:[:](\d+))?(?:[:](\d+))?$')
URL_SCHEME_PAT = re.compile(r'^[\w.+-]+://')
URL_PATH_PAT = re.compile(r'^(?:[\w.+-]+://|(?:www|(?:\w+\.)*(?:golang|pkgdoc|gosublime)\.org))')
HIST_EXPAND_PAT = re.compile(r'^(\^+)\s*(\d+)$')

HOURGLASS = u'\u231B'

DEFAULT_COMMANDS = [
	'help',
	'run',
	'build',
	'replay',
	'clear',
	'tskill',
	'tskill replay',
	'tskill go',
	'go',
	'go build',
	'go clean',
	'go doc',
	'go env',
	'go fix',
	'go fmt',
	'go get',
	'go install',
	'go list',
	'go run',
	'go test',
	'go tool',
	'go version',
	'go vet',
	'go help',
	'settings',
	'env',
	'share',
	'hist',
	'hist erase',
	'cd',
]
DEFAULT_CL = [(s, s+' ') for s in DEFAULT_COMMANDS]

stash = {}
tid_alias = {}

def active_wd(win=None):
	_, v = gs.win_view(win=win)
	return gs.basedir_or_cwd(v.file_name() if v else '')

def _hkey(wd):
	name = gs.setting("9o_instance")
	if name:
		wd = name
	return '9o.hist.%s' % wd

def _wdid(wd):
	name = gs.setting("9o_instance")
	if name:
		return name
	return '9o://%s' % wd


class EV(sublime_plugin.EventListener):
	def on_query_completions(self, view, prefix, locations):
		pos = gs.sel(view).begin()
		if view.score_selector(pos, 'text.9o') == 0:
			return []

		cl = set()

		hkey = _hkey(view.settings().get('9o.wd', ''))
		cl.update((k, k+' ') for k in gs.dval(gs.aso().get(hkey), []))
		cl.update((k, k+' ') for k in aliases())
		cl.update((k, k+' ') for k in builtins())
		cl.update(DEFAULT_CL)

		return ([cl_esc(e) for e in sorted(cl)], AC_OPTS)

def cl_esc(e):
	return (e[0], e[1].replace('$', '\\$'))

class Gs9oBuildCommand(sublime_plugin.WindowCommand):
	def is_enabled(self):
		view = gs.active_valid_go_view(self.window)
		return view is not None

	def run(self):
		view = self.window.active_view()
		args = {'run': gs.setting('build_command', ['^1'])} if gs.is_pkg_view(view) else {}
		view.run_command('gs9o_open', args)

class Gs9oInsertLineCommand(sublime_plugin.TextCommand):
	def run(self, edit, after=True):
		insln = lambda: self.view.insert(edit, gs.sel(self.view).begin(), "\n")
		if after:
			self.view.run_command("move_to", {"to": "hardeol"})
			insln()
		else:
			self.view.run_command("move_to", {"to": "hardbol"})
			insln()
			self.view.run_command("move", {"by": "lines", "forward": False})


class Gs9oMoveHist(sublime_plugin.TextCommand):
	def run(self, edit, up):
		view = self.view
		pos = gs.sel(view).begin()
		if view.score_selector(pos, 'prompt.9o') <= 0:
			return

		aso = gs.aso()
		vs = view.settings()
		wd = vs.get('9o.wd')
		hkey = _hkey(wd)
		hist = [s for s in gs.dval(aso.get(hkey), []) if s.strip()]
		if not hist:
			return

		r = view.extract_scope(pos)
		cmd = view.substr(r).strip('#').strip()
		try:
			idx = hist.index(cmd) + (-1 if up else 1)
			found = True
		except Exception:
			idx = -1
			found = False

		if cmd and not found:
			hist.append(cmd)
			aso.set(hkey, hist)
			gs.save_aso()

		if idx >= 0 and idx < len(hist):
			cmd = hist[idx]
		elif up:
			if not found:
				cmd = hist[-1]
		else:
			cmd = ''

		view.replace(edit, r, '# %s \n' % cmd)
		n = view.line(r.begin()).end()
		view.sel().clear()
		view.sel().add(sublime.Region(n, n))

class Gs9oInitCommand(sublime_plugin.TextCommand):
	def run(self, edit, wd=None):
		v = self.view
		vs = v.settings()

		if not wd:
			wd = vs.get('9o.wd', active_wd(win=v.window()))

		was_empty = v.size() == 0
		s = '[ %s ] # \n' % gs.simple_fn(wd).replace('#', '~')

		if was_empty:
			v.insert(edit, 0, 'GoSublime %s 9o: type `help` for help and command documentation\n\n' % about.VERSION)

		if was_empty or v.substr(v.size()-1) == '\n':
			v.insert(edit, v.size(), s)
		else:
			v.insert(edit, v.size(), '\n'+s)

		v.sel().clear()
		n = v.size()-1
		v.sel().add(sublime.Region(n, n))

		opts = {
			"rulers": [],
			"fold_buttons": True,
			"fade_fold_buttons": False,
			"gutter": True,
			"margin": 0,
			# pad mostly so the completion menu shows on the first line
			"line_padding_top": 1,
			"line_padding_bottom": 1,
			"tab_size": 2,
			"word_wrap": True,
			"indent_subsequent_lines": True,
			"line_numbers": False,
			"auto_complete": True,
			"auto_complete_selector": "text",
			"highlight_line": True,
			"draw_indent_guides": True,
			"scroll_past_end": True,
			"indent_guide_options": ["draw_normal", "draw_active"],
			"word_separators": "./\\()\"'-:,.;<>~!@#$%&*|+=[]{}`~?",
		}
		opts.update(gs.setting('9o_settings'))

		for opt in opts:
			vs.set(opt, opts[opt])

		vs.set("9o", True)
		vs.set("9o.wd", wd)

		color_scheme = gs.setting("9o_color_scheme", "")
		if color_scheme:
			if color_scheme == "default":
				vs.erase("color_scheme")
			else:
				vs.set("color_scheme", color_scheme)
		else:
			vs.set("color_scheme", "")

		v.set_syntax_file(gs.tm_path('9o'))

		if was_empty:
			v.show(0)
		else:
			v.show(v.size()-1)

		os.chdir(wd)

class Gs9oOpenCommand(sublime_plugin.TextCommand):
	def run(self, edit, wd=None, run=[], save_hist=False, focus_view=True):
		self.view.window().run_command('gs9o_win_open', {
			'wd': wd,
			'run': run,
			'save_hist': save_hist,
			'focus_view': focus_view,
		})

class Gs9oWinOpenCommand(sublime_plugin.WindowCommand):
	def run(self, wd=None, run=[], save_hist=False, focus_view=True):
		win = self.window
		wid = win.id()
		if not wd:
			wd = active_wd(win=win)

		id = _wdid(wd)
		st = stash.setdefault(wid, {})
		v = st.get(id)
		if v is None:
			v = win.get_output_panel(id)
			st[id] = v

		win.run_command("show_panel", {"panel": ("output.%s" % id)})

		if focus_view:
			win.focus_view(v)

		v.run_command('gs9o_init', {'wd': wd})

		if run:
			v.run_command('gs9o_paste_exec', {'cmd': ' '.join(run), 'save_hist': save_hist})

class Gs9oPasteExecCommand(sublime_plugin.TextCommand):
	def run(self, edit, cmd, save_hist=False):
		view = self.view
		view.insert(edit, view.line(view.size()-1).end(), cmd)
		view.sel().clear()
		view.sel().add(view.line(view.size()-1).end())
		view.run_command('gs9o_exec', {'save_hist': save_hist})

class Gs9oOpenSelectionCommand(sublime_plugin.TextCommand):
	def is_enabled(self):
		pos = gs.sel(self.view).begin()
		return self.view.score_selector(pos, 'text.9o') > 0

	def run(self, edit):
		actions = []
		v = self.view
		sel = gs.sel(v)
		if (sel.end() - sel.begin()) == 0:
			pos = sel.begin()
			inscope = lambda p: v.score_selector(p, 'path.9o') > 0
			if inscope(pos):
				actions.append(v.substr(v.extract_scope(pos)))
			else:
				pos -= 1
				if inscope(pos):
					actions.append(v.substr(v.extract_scope(pos)))
				else:
					line = v.line(pos)
					for cr in v.find_by_selector('path.9o'):
						if line.contains(cr):
							actions.append(v.substr(cr))
		else:
			actions.append(v.substr(sel))

		act_on(v, actions)

def act_on(view, actions):
	for a in actions:
		if act_on_path(view, a):
			break

def act_on_path(view, path):
	row = 0
	col = 0

	m = gs.VFN_ID_PAT.match(path)
	if m:
		path = 'gs.view://%s' % m.group(1)
		m2 = gs.ROWCOL_PAT.match(m.group(2))
		if m2:
			row = int(m2.group(1))-1 if m2.group(1) else 0