import os
import json
import re
import os.path
import filecmp
import pipes
import fnmatch
import subprocess
import sublime
import sublime_plugin
import tempfile
import hashlib
from shutil import copyfile

class SpyOnGemsCommand(sublime_plugin.WindowCommand):
    def __init__(self, window):
        self.settings = sublime.load_settings('gem-spy.sublime-settings')
        self.gems = []

        super(SpyOnGemsCommand, self).__init__(window)


    #
    #
    # Lifecycle

    def run(self, **kwargs):
        try:
            self.opts = kwargs
            self.gems = self.get_gems()
            self.window.show_quick_panel(self.gems, self.on_selected)
        except MissingGemfileLockException:
            message = "No Gemfile.lock in current directory"
            sublime.error_message(message)
            self.log(message)

    def on_selected(self, selected):
        if selected != -1:
            gem_name = re.search('(.*)\(', self.gems[selected]).group(1)
            gem_path = self.run_bundle_command("show " + gem_name)
            open_option = '-a' if self.opts['add_to_current_window'] else '-n'
            self.open_in_sublime([open_option, gem_path.rstrip()])



    #
    #
    # Gem fetching and caching

    def get_gems(self):
        project_name = self.window.folders()[0].split('/')[-1]
        cache_file_path = '/'.join([self.cache_directory(), project_name + "_gemfile.cache"])
        gemfile_lock_path = self.window.folders()[0] + "/Gemfile.lock"

        if not os.path.exists(gemfile_lock_path):
            raise MissingGemfileLockException

        gems_list = []

        try:
            with open(cache_file_path, 'r') as cache_file:
                cache_json = json.load(cache_file)
        except FileNotFoundError:
            cache_json = None

        gemfile_lock_md5 = str(self.md5(gemfile_lock_path))
        if self.cache_is_valid(cache_json, gemfile_lock_md5):
            gems_list = cache_json['gems']
        else:
            output = self.run_bundle_command('list')
            gems = str(output, encoding = 'utf-8')

            for gem in gems.split('\n'):
                gem_name = re.search('\* (.*)', gem)
                if gem_name:
                    gems_list.append(gem_name.group(1))

            cache   = { 'md5': gemfile_lock_md5, 'gems': gems_list }
            with open(cache_file_path, 'w') as cache_file:
                json.dump(cache, cache_file)

        return gems_list

    def cache_is_valid(self, cache_json, gemfile_lock_md5):
        cache_hit = False

        if cache_json:
            md5_present = 'md5' in cache_json
            gems_present = 'gems' in cache_json and isinstance(cache_json['gems'], list)
            cache_hit = md5_present and gems_present and cache_json['md5'] == gemfile_lock_md5

        log_message = "cache hit" if cache_hit else "cache miss"
        self.log(log_message)
        return cache_hit

    def cache_directory(self):
        sublime_cache = sublime.cache_path()
        cache_path = sublime_cache + "/gem-spy"

        if not os.path.exists(cache_path):
            os.makedirs(cache_path)

        return cache_path

    # http://stackoverflow.com/a/3431838/1026980
    def md5(self, fname):
        hash_md5 = hashlib.md5()
        with open(fname, "rb") as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash_md5.update(chunk)
        return hash_md5.hexdigest()



    #
    #
    # Utilities

    def run_bundle_command(self, command):
        current_path = self.window.folders()[0]
        rbenv_command = os.path.expanduser('~/.rbenv/shims/bundle ' + command)
        process = subprocess.Popen(rbenv_command.split(), cwd=current_path, stdout=subprocess.PIPE)
        output = process.communicate()[0]
        return output

    def open_in_sublime(self, args):
        try:
            args.insert(0, self.settings.get('sublime_path'))
            subprocess.Popen(args)
        except FileNotFoundError:
            error = "Could not find Sublime Executable. Check the sublime_path in your gem-spy settings."
            self.log(error)

    def log(self, message):
        print("Gem Spy Logger: " + message)


class MissingGemfileLockException(Exception):
    pass

@@ -707,26 +707,25 @@
         
-message = 
+self.log(
 %22No Gemf
@@ -758,79 +758,118 @@
 ory%22
-%0A            sublime.error_message(message)%0A            self.log(messag
+, error=True)%0A        except BadBundlerPathException:%0A            self.log(%22Could not find bundler%22, error=Tru
 e)%0A%0A
@@ -3534,24 +3534,41 @@
 , command):%0A
+        try:%0A    
         curr
@@ -3599,24 +3599,28 @@
 olders()%5B0%5D%0A
+    
         rben
@@ -3694,16 +3694,20 @@
         
+    
 process 
@@ -3790,32 +3790,36 @@
 s.PIPE)%0A        
+   
+ 
 output = process
@@ -3840,24 +3840,28 @@
 %5B0%5D%0A        
+    
 return outpu
@@ -3861,16 +3861,92 @@
 n output
+%0A        except FileNotFoundError:%0A            raise BadBundlerPathException
 %0A%0A    de
@@ -4280,18 +4280,92 @@
  message
+, error=False):%0A        if error:%0A            sublime.error_message(message
 )
-:
 %0A       
@@ -4458,8 +4458,60 @@
    pass%0A
+%0Aclass BadBundlerPathException(Exception):%0A    pass%0A

Handle bad bundler path exceptions